diff options
35 files changed, 905 insertions, 512 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index bf0ef9465fcd..2c2c2953ed51 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1122,6 +1122,9 @@ public abstract class Layout { if (limit > lineEnd) { limit = lineEnd; } + if (limit == start) { + continue; + } level[limit - lineStart - 1] = (byte) ((runs[i + 1] >>> RUN_LEVEL_SHIFT) & RUN_LEVEL_MASK); } @@ -1220,8 +1223,8 @@ public abstract class Layout { } /** - * Computes in linear time the results of calling - * #getHorizontal for all offsets on a line. + * Computes in linear time the results of calling #getHorizontal for all offsets on a line. + * * @param line The line giving the offsets we compute information for * @param clamped Whether to clamp the results to the width of the layout * @param primary Whether the results should be the primary or the secondary horizontal @@ -1257,7 +1260,7 @@ public abstract class Layout { TextLine.recycle(tl); if (clamped) { - for (int offset = 0; offset <= wid.length; ++offset) { + for (int offset = 0; offset < wid.length; ++offset) { if (wid[offset] > mWidth) { wid[offset] = mWidth; } diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 8b70bd61785a..4daddfbfb204 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -177,6 +177,11 @@ public final class SystemUiDeviceConfigFlags { "assist_handles_suppress_on_apps"; /** + * (bool) Whether to show handles when taught. + */ + public static final String ASSIST_HANDLES_SHOW_WHEN_TAUGHT = "assist_handles_show_when_taught"; + + /** * (bool) Whether to use the new BrightLineFalsingManager. */ public static final String BRIGHTLINE_FALSING_MANAGER_ENABLED = diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java index 82593b5e2f0a..1fdb1f30125f 100755 --- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java +++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java @@ -458,7 +458,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, } private String getFuncTypeAsString() { - if (isRecycled()) throw new IllegalStateException(); + if (isRecycled()) return "<recycled>"; if (isConstSupplier()) return "supplier"; String name = LambdaType.toString(getFlags(MASK_EXPOSED_AS)); if (name.endsWith("Consumer")) return "consumer"; @@ -466,7 +466,7 @@ final class PooledLambdaImpl<R> extends OmniFunction<Object, if (name.endsWith("Predicate")) return "predicate"; if (name.endsWith("Supplier")) return "supplier"; if (name.endsWith("Runnable")) return "runnable"; - throw new IllegalStateException("Don't know the string representation of " + name); + return name; } /** diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 4783a257755f..82c27f02ba87 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1160,6 +1160,7 @@ static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gi /* * Grant the following capabilities to the Bluetooth user: * - CAP_WAKE_ALARM + * - CAP_NET_ADMIN * - CAP_NET_RAW * - CAP_NET_BIND_SERVICE (for DHCP client functionality) * - CAP_SYS_NICE (for setting RT priority for audio-related threads) @@ -1167,6 +1168,7 @@ static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gi if (multiuser_get_app_id(uid) == AID_BLUETOOTH) { capabilities |= (1LL << CAP_WAKE_ALARM); + capabilities |= (1LL << CAP_NET_ADMIN); capabilities |= (1LL << CAP_NET_RAW); capabilities |= (1LL << CAP_NET_BIND_SERVICE); capabilities |= (1LL << CAP_SYS_NICE); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6aff8b8f1d5c..fef4dcd3b9f1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2544,7 +2544,7 @@ <!-- @SystemApi @TestApi @hide Allows an application to modify config settings. <p>Not for use by third-party applications. --> <permission android:name="android.permission.WRITE_DEVICE_CONFIG" - android:protectionLevel="signature|configurator"/> + android:protectionLevel="signature|verifier|configurator"/> <!-- @SystemApi @hide Allows an application to read config settings. <p>Not for use by third-party applications. --> diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java index 990161a88c22..93a6b15f744c 100644 --- a/core/tests/coretests/src/android/text/LayoutTest.java +++ b/core/tests/coretests/src/android/text/LayoutTest.java @@ -743,6 +743,9 @@ public class LayoutTest { assertPrimaryIsTrailingPrevious( RTL + LRI + RTL + LTR + PDI + RTL, new boolean[]{false, false, true, false, false, false, false}); + assertPrimaryIsTrailingPrevious( + "", + new boolean[]{false}); } } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 4b4912cb1e8a..17274f418a1e 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -644,7 +644,8 @@ <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" android:authorities="com.android.keyguard.clock" - android:exported="true" + android:enabled="false" + android:exported="false" android:grantUriPermissions="true"> </provider> diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java index 0af333e96edf..f9ddeaef3e86 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java @@ -67,6 +67,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { private static final boolean DEFAULT_SUPPRESS_ON_LOCKSCREEN = false; private static final boolean DEFAULT_SUPPRESS_ON_LAUNCHER = false; private static final boolean DEFAULT_SUPPRESS_ON_APPS = true; + private static final boolean DEFAULT_SHOW_WHEN_TAUGHT = false; private static final String[] DEFAULT_HOME_CHANGE_ACTIONS = new String[] { PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED, @@ -309,7 +310,7 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { return; } - if (mIsDozing || mIsNavBarHidden || mOnLockscreen) { + if (mIsDozing || mIsNavBarHidden || mOnLockscreen || !getShowWhenTaught()) { mAssistHandleCallbacks.hide(); } else if (justUnlocked) { long currentEpochDay = LocalDate.now().toEpochDay(); @@ -429,6 +430,12 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { DEFAULT_SUPPRESS_ON_APPS); } + private boolean getShowWhenTaught() { + return mPhenotypeHelper.getBoolean( + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT, + DEFAULT_SHOW_WHEN_TAUGHT); + } + @Override public void dump(PrintWriter pw, String prefix) { pw.println(prefix + "Current AssistHandleReminderExpBehavior State:"); @@ -480,5 +487,9 @@ final class AssistHandleReminderExpBehavior implements BehaviorController { + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SUPPRESS_ON_APPS + "=" + getSuppressOnApps()); + pw.println(prefix + " " + + SystemUiDeviceConfigFlags.ASSIST_HANDLES_SHOW_WHEN_TAUGHT + + "=" + + getShowWhenTaught()); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 15218896f089..3cc8ec9afbb2 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -96,7 +96,7 @@ public class FalsingManagerProxy implements FalsingManager { mInternalFalsingManager = new FalsingManagerImpl(context); } else { mInternalFalsingManager = new BrightLineFalsingManager( - new FalsingDataProvider(context), + new FalsingDataProvider(context.getResources().getDisplayMetrics()), Dependency.get(AsyncSensorManager.class) ); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java index 4975e63f7a0a..8b11ceb34ea0 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java @@ -16,7 +16,6 @@ package com.android.systemui.classifier.brightline; -import android.content.Context; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; @@ -51,8 +50,7 @@ public class FalsingDataProvider { private MotionEvent mFirstRecentMotionEvent; private MotionEvent mLastMotionEvent; - public FalsingDataProvider(Context context) { - DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); + public FalsingDataProvider(DisplayMetrics displayMetrics) { mXdpi = displayMetrics.xdpi; mYdpi = displayMetrics.ydpi; mWidthPixels = displayMetrics.widthPixels; @@ -145,12 +143,20 @@ public class FalsingDataProvider { boolean isHorizontal() { recalculateData(); + if (mRecentMotionEvents.isEmpty()) { + return false; + } + return Math.abs(mFirstRecentMotionEvent.getX() - mLastMotionEvent.getX()) > Math .abs(mFirstRecentMotionEvent.getY() - mLastMotionEvent.getY()); } boolean isRight() { recalculateData(); + if (mRecentMotionEvents.isEmpty()) { + return false; + } + return mLastMotionEvent.getX() > mFirstRecentMotionEvent.getX(); } @@ -160,6 +166,10 @@ public class FalsingDataProvider { boolean isUp() { recalculateData(); + if (mRecentMotionEvents.isEmpty()) { + return false; + } + return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY(); } @@ -168,8 +178,13 @@ public class FalsingDataProvider { return; } - mFirstRecentMotionEvent = mRecentMotionEvents.get(0); - mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1); + if (mRecentMotionEvents.isEmpty()) { + mFirstRecentMotionEvent = null; + mLastMotionEvent = null; + } else { + mFirstRecentMotionEvent = mRecentMotionEvents.get(0); + mLastMotionEvent = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1); + } calculateAngleInternal(); @@ -245,5 +260,7 @@ public class FalsingDataProvider { } mRecentMotionEvents.clear(); + + mDirty = true; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java new file mode 100644 index 000000000000..d011e486d2e0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2019 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.systemui.classifier.brightline; + +import android.util.DisplayMetrics; +import android.view.MotionEvent; + +import com.android.systemui.SysuiTestCase; + +import org.junit.After; +import org.junit.Before; + +import java.util.ArrayList; +import java.util.List; + +public class ClassifierTest extends SysuiTestCase { + + private FalsingDataProvider mDataProvider; + private List<MotionEvent> mMotionEvents = new ArrayList<>(); + private float mOffsetX = 0; + private float mOffsetY = 0; + + @Before + public void setup() { + DisplayMetrics displayMetrics = new DisplayMetrics(); + displayMetrics.xdpi = 100; + displayMetrics.ydpi = 100; + displayMetrics.widthPixels = 1000; + displayMetrics.heightPixels = 1000; + mDataProvider = new FalsingDataProvider(displayMetrics); + } + + @After + public void tearDown() { + resetDataProvider(); + } + + FalsingDataProvider getDataProvider() { + return mDataProvider; + } + + void setOffsetX(float offsetX) { + mOffsetX = offsetX; + } + + void setOffsetY(float offsetY) { + mOffsetY = offsetY; + } + + void resetDataProvider() { + for (MotionEvent motionEvent : mMotionEvents) { + motionEvent.recycle(); + } + + mMotionEvents.clear(); + + mDataProvider.onSessionEnd(); + } + + MotionEvent appendDownEvent(float x, float y) { + return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y); + } + + MotionEvent appendDownEvent(float x, float y, long eventTime) { + return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y, eventTime); + } + + MotionEvent appendMoveEvent(float x, float y) { + return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y); + } + + MotionEvent appendMoveEvent(float x, float y, long eventTime) { + return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y, eventTime); + } + + + MotionEvent appendUpEvent(float x, float y) { + return appendMotionEvent(MotionEvent.ACTION_UP, x, y); + } + + MotionEvent appendUpEvent(float x, float y, long eventTime) { + return appendMotionEvent(MotionEvent.ACTION_UP, x, y, eventTime); + } + + private MotionEvent appendMotionEvent(int actionType, float x, float y) { + + long eventTime = mMotionEvents.isEmpty() ? 1 : mMotionEvents.get( + mMotionEvents.size() - 1).getEventTime() + 1; + return appendMotionEvent(actionType, x, y, eventTime); + } + + private MotionEvent appendMotionEvent(int actionType, float x, float y, long eventTime) { + x += mOffsetX; + y += mOffsetY; + + MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, actionType, x, y, + 0); + mMotionEvents.add(motionEvent); + + mDataProvider.onMotionEvent(motionEvent); + + return motionEvent; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java index ade5f36d659e..b45d3f2855ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java @@ -28,8 +28,7 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.SysuiTestCase; - +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,7 +38,7 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class DiagonalClassifierTest extends SysuiTestCase { +public class DiagonalClassifierTest extends ClassifierTest { // Next variable is not actually five, but is very close. 5 degrees is currently the value // used in the diagonal classifier, so we want slightly less than that to deal with @@ -57,10 +56,16 @@ public class DiagonalClassifierTest extends SysuiTestCase { @Before public void setup() { + super.setup(); MockitoAnnotations.initMocks(this); mClassifier = new DiagonalClassifier(mDataProvider); } + @After + public void tearDown() { + super.tearDown(); + } + @Test public void testPass_UnknownAngle() { when(mDataProvider.getAngle()).thenReturn(Float.MAX_VALUE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java index 3d0471bee728..805bb91591e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java @@ -18,48 +18,37 @@ package com.android.systemui.classifier.brightline; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.SysuiTestCase; - +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class DistanceClassifierTest extends SysuiTestCase { +public class DistanceClassifierTest extends ClassifierTest { - @Mock private FalsingDataProvider mDataProvider; private FalsingClassifier mClassifier; - private List<MotionEvent> mMotionEvents = new ArrayList<>(); - - private static final float DPI = 100; - private static final int SCREEN_SIZE = (int) (DPI * 10); @Before public void setup() { - MockitoAnnotations.initMocks(this); - when(mDataProvider.getHeightPixels()).thenReturn(SCREEN_SIZE); - when(mDataProvider.getWidthPixels()).thenReturn(SCREEN_SIZE); - when(mDataProvider.getXdpi()).thenReturn(DPI); - when(mDataProvider.getYdpi()).thenReturn(DPI); + super.setup(); + mDataProvider = getDataProvider(); mClassifier = new DistanceClassifier(mDataProvider); } + @After + public void tearDown() { + super.tearDown(); + } + @Test public void testPass_noPointer() { assertThat(mClassifier.isFalseTouch(), is(true)); @@ -67,104 +56,54 @@ public class DistanceClassifierTest extends SysuiTestCase { @Test public void testPass_fling() { - MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0); - MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0); - MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 40, 0); - appendMotionEvent(motionEventA); + mClassifier.onTouchEvent(appendDownEvent(1, 1)); assertThat(mClassifier.isFalseTouch(), is(true)); - appendMotionEvent(motionEventB); + mClassifier.onTouchEvent(appendMoveEvent(1, 2)); assertThat(mClassifier.isFalseTouch(), is(true)); - appendMotionEvent(motionEventC); + mClassifier.onTouchEvent(appendUpEvent(1, 40)); assertThat(mClassifier.isFalseTouch(), is(false)); - - motionEventA.recycle(); - motionEventB.recycle(); - motionEventC.recycle(); } @Test public void testFail_flingShort() { - MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0); - MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 2, 0); - MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_UP, 1, 10, 0); - - appendMotionEvent(motionEventA); + mClassifier.onTouchEvent(appendDownEvent(1, 1)); assertThat(mClassifier.isFalseTouch(), is(true)); - appendMotionEvent(motionEventB); + mClassifier.onTouchEvent(appendMoveEvent(1, 2)); assertThat(mClassifier.isFalseTouch(), is(true)); - appendMotionEvent(motionEventC); + mClassifier.onTouchEvent(appendUpEvent(1, 10)); assertThat(mClassifier.isFalseTouch(), is(true)); - - motionEventA.recycle(); - motionEventB.recycle(); - motionEventC.recycle(); } @Test public void testFail_flingSlowly() { // These events, in testing, result in a fling that falls just short of the threshold. - MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0); - MotionEvent motionEventB = MotionEvent.obtain(1, 2, MotionEvent.ACTION_MOVE, 1, 15, 0); - MotionEvent motionEventC = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, 16, 0); - MotionEvent motionEventD = MotionEvent.obtain(1, 300, MotionEvent.ACTION_MOVE, 1, 17, 0); - MotionEvent motionEventE = MotionEvent.obtain(1, 301, MotionEvent.ACTION_MOVE, 1, 18, 0); - MotionEvent motionEventF = MotionEvent.obtain(1, 500, MotionEvent.ACTION_UP, 1, 19, 0); - - appendMotionEvent(motionEventA); - assertThat(mClassifier.isFalseTouch(), is(true)); - appendMotionEvent(motionEventB); + mClassifier.onTouchEvent(appendDownEvent(1, 1, 1)); assertThat(mClassifier.isFalseTouch(), is(true)); - appendMotionEvent(motionEventC); - appendMotionEvent(motionEventD); - appendMotionEvent(motionEventE); - appendMotionEvent(motionEventF); + mClassifier.onTouchEvent(appendMoveEvent(1, 15, 2)); assertThat(mClassifier.isFalseTouch(), is(true)); - motionEventA.recycle(); - motionEventB.recycle(); - motionEventC.recycle(); - motionEventD.recycle(); - motionEventE.recycle(); - motionEventF.recycle(); + mClassifier.onTouchEvent(appendMoveEvent(1, 16, 3)); + mClassifier.onTouchEvent(appendMoveEvent(1, 17, 300)); + mClassifier.onTouchEvent(appendMoveEvent(1, 18, 301)); + mClassifier.onTouchEvent(appendUpEvent(1, 19, 501)); + assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void testPass_swipe() { - MotionEvent motionEventA = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0); - MotionEvent motionEventB = MotionEvent.obtain(1, 3, MotionEvent.ACTION_MOVE, 1, DPI * 3, 0); - MotionEvent motionEventC = MotionEvent.obtain(1, 1000, MotionEvent.ACTION_UP, 1, DPI * 3, - 0); - appendMotionEvent(motionEventA); + mClassifier.onTouchEvent(appendDownEvent(1, 1)); assertThat(mClassifier.isFalseTouch(), is(true)); - - appendMotionEvent(motionEventB); - appendMotionEvent(motionEventC); + mClassifier.onTouchEvent(appendMoveEvent(1, mDataProvider.getYdpi() * 3, 3)); + mClassifier.onTouchEvent(appendUpEvent(1, mDataProvider.getYdpi() * 3, 300)); assertThat(mClassifier.isFalseTouch(), is(false)); - - motionEventA.recycle(); - motionEventB.recycle(); - motionEventC.recycle(); - } - - private void appendMotionEvent(MotionEvent motionEvent) { - if (mMotionEvents.isEmpty()) { - when(mDataProvider.getFirstRecentMotionEvent()).thenReturn(motionEvent); - } - - mMotionEvents.add(motionEvent); - when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents); - - when(mDataProvider.getLastMotionEvent()).thenReturn(motionEvent); - - mClassifier.onTouchEvent(motionEvent); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java index 1da42061c234..748c1377f7a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java @@ -22,12 +22,12 @@ import static org.junit.Assert.assertThat; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.util.DisplayMetrics; import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.SysuiTestCase; - +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,24 +37,32 @@ import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class FalsingDataProviderTest extends SysuiTestCase { +public class FalsingDataProviderTest extends ClassifierTest { private FalsingDataProvider mDataProvider; @Before public void setup() { - mDataProvider = new FalsingDataProvider(getContext()); + super.setup(); + DisplayMetrics displayMetrics = new DisplayMetrics(); + displayMetrics.xdpi = 100; + displayMetrics.ydpi = 100; + displayMetrics.widthPixels = 1000; + displayMetrics.heightPixels = 1000; + mDataProvider = new FalsingDataProvider(displayMetrics); + } + + @After + public void tearDown() { + super.tearDown(); + mDataProvider.onSessionEnd(); } @Test public void test_trackMotionEvents() { - MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9); - MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7); - MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 3, 6, 5); - - mDataProvider.onMotionEvent(motionEventA); - mDataProvider.onMotionEvent(motionEventB); - mDataProvider.onMotionEvent(motionEventC); + mDataProvider.onMotionEvent(appendDownEvent(2, 9)); + mDataProvider.onMotionEvent(appendMoveEvent(4, 7)); + mDataProvider.onMotionEvent(appendUpEvent(6, 5)); List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents(); assertThat(motionEventList.size(), is(3)); @@ -70,20 +78,12 @@ public class FalsingDataProviderTest extends SysuiTestCase { assertThat(motionEventList.get(0).getY(), is(9f)); assertThat(motionEventList.get(1).getY(), is(7f)); assertThat(motionEventList.get(2).getY(), is(5f)); - - motionEventA.recycle(); - motionEventB.recycle(); - motionEventC.recycle(); } @Test public void test_trackRecentMotionEvents() { - MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 2, 9); - MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 800, 4, 7); - MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_UP, 1200, 6, 5); - - mDataProvider.onMotionEvent(motionEventA); - mDataProvider.onMotionEvent(motionEventB); + mDataProvider.onMotionEvent(appendDownEvent(2, 9, 1)); + mDataProvider.onMotionEvent(appendMoveEvent(4, 7, 800)); List<MotionEvent> motionEventList = mDataProvider.getRecentMotionEvents(); assertThat(motionEventList.size(), is(2)); @@ -96,7 +96,7 @@ public class FalsingDataProviderTest extends SysuiTestCase { assertThat(motionEventList.get(0).getY(), is(9f)); assertThat(motionEventList.get(1).getY(), is(7f)); - mDataProvider.onMotionEvent(motionEventC); + mDataProvider.onMotionEvent(appendUpEvent(6, 5, 1200)); // Still two events, but event a is gone. assertThat(motionEventList.size(), is(2)); @@ -115,18 +115,14 @@ public class FalsingDataProviderTest extends SysuiTestCase { assertThat(firstRealMotionEvent.getEventTime(), is(1L)); assertThat(firstRealMotionEvent.getX(), is(2f)); assertThat(firstRealMotionEvent.getY(), is(9f)); - - motionEventA.recycle(); - motionEventB.recycle(); - motionEventC.recycle(); } @Test public void test_unpackMotionEvents() { // Batching only works for motion events of the same type. - MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 1, 2, 9); - MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 4, 7); - MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 3, 6, 5); + MotionEvent motionEventA = appendMoveEvent(2, 9); + MotionEvent motionEventB = appendMoveEvent(4, 7); + MotionEvent motionEventC = appendMoveEvent(6, 5); motionEventA.addBatch(motionEventB); motionEventA.addBatch(motionEventC); // Note that calling addBatch changes properties on the original event, not just it's @@ -148,114 +144,86 @@ public class FalsingDataProviderTest extends SysuiTestCase { assertThat(motionEventList.get(0).getY(), is(9f)); assertThat(motionEventList.get(1).getY(), is(7f)); assertThat(motionEventList.get(2).getY(), is(5f)); - - motionEventA.recycle(); - motionEventB.recycle(); - motionEventC.recycle(); } @Test public void test_getAngle() { - MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0); + MotionEvent motionEventOrigin = appendDownEvent(0, 0); - MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventA); + mDataProvider.onMotionEvent(appendMoveEvent(1, 1)); assertThat((double) mDataProvider.getAngle(), closeTo(Math.PI / 4, .001)); - motionEventA.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -1, -1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventB); + mDataProvider.onMotionEvent(appendMoveEvent(-1, -1)); assertThat((double) mDataProvider.getAngle(), closeTo(5 * Math.PI / 4, .001)); - motionEventB.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 0); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventC); + mDataProvider.onMotionEvent(appendMoveEvent(2, 0)); assertThat((double) mDataProvider.getAngle(), closeTo(0, .001)); - motionEventC.recycle(); mDataProvider.onSessionEnd(); } @Test public void test_isHorizontal() { - MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0); + MotionEvent motionEventOrigin = appendDownEvent(0, 0); - MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventA); + mDataProvider.onMotionEvent(appendMoveEvent(1, 1)); assertThat(mDataProvider.isHorizontal(), is(false)); - motionEventA.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 2, 1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventB); + mDataProvider.onMotionEvent(appendMoveEvent(2, 1)); assertThat(mDataProvider.isHorizontal(), is(true)); - motionEventB.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventC); + mDataProvider.onMotionEvent(appendMoveEvent(-3, -1)); assertThat(mDataProvider.isHorizontal(), is(true)); - motionEventC.recycle(); mDataProvider.onSessionEnd(); } @Test public void test_isVertical() { - MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0); + MotionEvent motionEventOrigin = appendDownEvent(0, 0); - MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 0); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventA); + mDataProvider.onMotionEvent(appendMoveEvent(1, 0)); assertThat(mDataProvider.isVertical(), is(false)); - motionEventA.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventB); + mDataProvider.onMotionEvent(appendMoveEvent(0, 1)); assertThat(mDataProvider.isVertical(), is(true)); - motionEventB.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventC); + mDataProvider.onMotionEvent(appendMoveEvent(-3, -10)); assertThat(mDataProvider.isVertical(), is(true)); - motionEventC.recycle(); mDataProvider.onSessionEnd(); } @Test public void test_isRight() { - MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0); + MotionEvent motionEventOrigin = appendDownEvent(0, 0); - MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, 1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventA); + mDataProvider.onMotionEvent(appendMoveEvent(1, 1)); assertThat(mDataProvider.isRight(), is(true)); - motionEventA.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventB); + mDataProvider.onMotionEvent(appendMoveEvent(0, 1)); assertThat(mDataProvider.isRight(), is(false)); - motionEventB.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, -10); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventC); + mDataProvider.onMotionEvent(appendMoveEvent(-3, -10)); assertThat(mDataProvider.isRight(), is(false)); - motionEventC.recycle(); mDataProvider.onSessionEnd(); } @@ -263,31 +231,21 @@ public class FalsingDataProviderTest extends SysuiTestCase { public void test_isUp() { // Remember that our y axis is flipped. - MotionEvent motionEventOrigin = obtainMotionEvent(MotionEvent.ACTION_DOWN, 1, 0, 0); + MotionEvent motionEventOrigin = appendDownEvent(0, 0); - MotionEvent motionEventA = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 1, -1); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventA); + mDataProvider.onMotionEvent(appendMoveEvent(1, -1)); assertThat(mDataProvider.isUp(), is(true)); - motionEventA.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventB = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, 0, 0); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventB); + mDataProvider.onMotionEvent(appendMoveEvent(0, 0)); assertThat(mDataProvider.isUp(), is(false)); - motionEventB.recycle(); mDataProvider.onSessionEnd(); - MotionEvent motionEventC = obtainMotionEvent(MotionEvent.ACTION_MOVE, 2, -3, 10); mDataProvider.onMotionEvent(motionEventOrigin); - mDataProvider.onMotionEvent(motionEventC); + mDataProvider.onMotionEvent(appendMoveEvent(-3, 10)); assertThat(mDataProvider.isUp(), is(false)); - motionEventC.recycle(); mDataProvider.onSessionEnd(); } - - private MotionEvent obtainMotionEvent(int action, long eventTimeMs, float x, float y) { - return MotionEvent.obtain(1, eventTimeMs, action, x, y, 0); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java index cba9ee38b306..341b74b33784 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java @@ -25,25 +25,27 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.SysuiTestCase; - +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class PointerCountClassifierTest extends SysuiTestCase { +public class PointerCountClassifierTest extends ClassifierTest { - @Mock - private FalsingDataProvider mDataProvider; private FalsingClassifier mClassifier; @Before public void setup() { - mClassifier = new PointerCountClassifier(mDataProvider); + super.setup(); + mClassifier = new PointerCountClassifier(getDataProvider()); + } + + @After + public void tearDown() { + super.tearDown(); } @Test @@ -53,9 +55,7 @@ public class PointerCountClassifierTest extends SysuiTestCase { @Test public void testPass_singlePointer() { - MotionEvent motionEvent = MotionEvent.obtain(1, 1, MotionEvent.ACTION_DOWN, 1, 1, 0); - mClassifier.onTouchEvent(motionEvent); - motionEvent.recycle(); + mClassifier.onTouchEvent(appendDownEvent(1, 1)); assertThat(mClassifier.isFalseTouch(), is(false)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java index 2ed792542efd..a6cabbf49458 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java @@ -31,8 +31,7 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.SysuiTestCase; - +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,7 +44,7 @@ import java.lang.reflect.Field; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class ProximityClassifierTest extends SysuiTestCase { +public class ProximityClassifierTest extends ClassifierTest { private static final long NS_PER_MS = 1000000; @@ -57,12 +56,18 @@ public class ProximityClassifierTest extends SysuiTestCase { @Before public void setup() { + super.setup(); MockitoAnnotations.initMocks(this); when(mDataProvider.getInteractionType()).thenReturn(GENERIC); when(mDistanceClassifier.isLongSwipe()).thenReturn(false); mClassifier = new ProximityClassifier(mDistanceClassifier, mDataProvider); } + @After + public void tearDown() { + super.tearDown(); + } + @Test public void testPass_uncovered() { touchDown(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java index 4bb3c15818c1..0355dc362593 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java @@ -34,8 +34,6 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; -import com.android.systemui.SysuiTestCase; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,15 +43,15 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class TypeClassifierTest extends SysuiTestCase { +public class TypeClassifierTest extends ClassifierTest { @Mock private FalsingDataProvider mDataProvider; - private FalsingClassifier mClassifier; @Before public void setup() { + super.setup(); MockitoAnnotations.initMocks(this); mClassifier = new TypeClassifier(mDataProvider); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java index 9d09a7e6f01f..25a1a75b0cbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java @@ -18,128 +18,93 @@ package com.android.systemui.classifier.brightline; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.when; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.SysuiTestCase; - import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; -import java.util.ArrayList; -import java.util.List; import java.util.Random; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class ZigZagClassifierTest extends SysuiTestCase { - - private static final long NS_PER_MS = 1000000; +public class ZigZagClassifierTest extends ClassifierTest { - @Mock - private FalsingDataProvider mDataProvider; private FalsingClassifier mClassifier; - private List<MotionEvent> mMotionEvents = new ArrayList<>(); - private float mOffsetX = 0; - private float mOffsetY = 0; - private float mDx; - private float mDy; @Before public void setup() { - MockitoAnnotations.initMocks(this); - when(mDataProvider.getXdpi()).thenReturn(100f); - when(mDataProvider.getYdpi()).thenReturn(100f); - when(mDataProvider.getRecentMotionEvents()).thenReturn(mMotionEvents); - mClassifier = new ZigZagClassifier(mDataProvider); - - - // Calculate the response to these calls on the fly, otherwise Mockito gets bogged down - // everytime we call appendMotionEvent. - when(mDataProvider.getFirstRecentMotionEvent()).thenAnswer( - (Answer<MotionEvent>) invocation -> mMotionEvents.get(0)); - when(mDataProvider.getLastMotionEvent()).thenAnswer( - (Answer<MotionEvent>) invocation -> mMotionEvents.get(mMotionEvents.size() - 1)); - when(mDataProvider.isHorizontal()).thenAnswer( - (Answer<Boolean>) invocation -> Math.abs(mDy) < Math.abs(mDx)); - when(mDataProvider.isVertical()).thenAnswer( - (Answer<Boolean>) invocation -> Math.abs(mDy) > Math.abs(mDx)); - when(mDataProvider.isRight()).thenAnswer((Answer<Boolean>) invocation -> mDx > 0); - when(mDataProvider.isUp()).thenAnswer((Answer<Boolean>) invocation -> mDy < 0); + super.setup(); + mClassifier = new ZigZagClassifier(getDataProvider()); } @After public void tearDown() { - clearMotionEvents(); + super.tearDown(); } @Test public void testPass_fewTouchesVertical() { assertThat(mClassifier.isFalseTouch(), is(false)); - appendMotionEvent(0, 0); + appendMoveEvent(0, 0); assertThat(mClassifier.isFalseTouch(), is(false)); - appendMotionEvent(0, 100); + appendMoveEvent(0, 100); assertThat(mClassifier.isFalseTouch(), is(false)); } @Test public void testPass_vertical() { - appendMotionEvent(0, 0); - appendMotionEvent(0, 100); - appendMotionEvent(0, 200); + appendMoveEvent(0, 0); + appendMoveEvent(0, 100); + appendMoveEvent(0, 200); assertThat(mClassifier.isFalseTouch(), is(false)); } @Test public void testPass_fewTouchesHorizontal() { assertThat(mClassifier.isFalseTouch(), is(false)); - appendMotionEvent(0, 0); + appendMoveEvent(0, 0); assertThat(mClassifier.isFalseTouch(), is(false)); - appendMotionEvent(100, 0); + appendMoveEvent(100, 0); assertThat(mClassifier.isFalseTouch(), is(false)); } @Test public void testPass_horizontal() { - appendMotionEvent(0, 0); - appendMotionEvent(100, 0); - appendMotionEvent(200, 0); + appendMoveEvent(0, 0); + appendMoveEvent(100, 0); + appendMoveEvent(200, 0); assertThat(mClassifier.isFalseTouch(), is(false)); } @Test public void testFail_minimumTouchesVertical() { - appendMotionEvent(0, 0); - appendMotionEvent(0, 100); - appendMotionEvent(0, 1); + appendMoveEvent(0, 0); + appendMoveEvent(0, 100); + appendMoveEvent(0, 1); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void testFail_minimumTouchesHorizontal() { - appendMotionEvent(0, 0); - appendMotionEvent(100, 0); - appendMotionEvent(1, 0); + appendMoveEvent(0, 0); + appendMoveEvent(100, 0); + appendMoveEvent(1, 0); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void testPass_fortyFiveDegreesStraight() { - appendMotionEvent(0, 0); - appendMotionEvent(10, 10); - appendMotionEvent(20, 20); + appendMoveEvent(0, 0); + appendMoveEvent(10, 10); + appendMoveEvent(20, 20); assertThat(mClassifier.isFalseTouch(), is(false)); } @@ -147,9 +112,9 @@ public class ZigZagClassifierTest extends SysuiTestCase { public void testPass_horizontalZigZagVerticalStraight() { // This test looks just like testFail_horizontalZigZagVerticalStraight but with // a longer y range, making it look straighter. - appendMotionEvent(0, 0); - appendMotionEvent(5, 100); - appendMotionEvent(-5, 200); + appendMoveEvent(0, 0); + appendMoveEvent(5, 100); + appendMoveEvent(-5, 200); assertThat(mClassifier.isFalseTouch(), is(false)); } @@ -157,9 +122,9 @@ public class ZigZagClassifierTest extends SysuiTestCase { public void testPass_horizontalStraightVerticalZigZag() { // This test looks just like testFail_horizontalStraightVerticalZigZag but with // a longer x range, making it look straighter. - appendMotionEvent(0, 0); - appendMotionEvent(100, 5); - appendMotionEvent(200, -5); + appendMoveEvent(0, 0); + appendMoveEvent(100, 5); + appendMoveEvent(200, -5); assertThat(mClassifier.isFalseTouch(), is(false)); } @@ -167,9 +132,9 @@ public class ZigZagClassifierTest extends SysuiTestCase { public void testFail_horizontalZigZagVerticalStraight() { // This test looks just like testPass_horizontalZigZagVerticalStraight but with // a shorter y range, making it look more crooked. - appendMotionEvent(0, 0); - appendMotionEvent(5, 10); - appendMotionEvent(-5, 20); + appendMoveEvent(0, 0); + appendMoveEvent(5, 10); + appendMoveEvent(-5, 20); assertThat(mClassifier.isFalseTouch(), is(true)); } @@ -177,217 +142,217 @@ public class ZigZagClassifierTest extends SysuiTestCase { public void testFail_horizontalStraightVerticalZigZag() { // This test looks just like testPass_horizontalStraightVerticalZigZag but with // a shorter x range, making it look more crooked. - appendMotionEvent(0, 0); - appendMotionEvent(10, 5); - appendMotionEvent(20, -5); + appendMoveEvent(0, 0); + appendMoveEvent(10, 5); + appendMoveEvent(20, -5); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void test_between0And45() { - appendMotionEvent(0, 0); - appendMotionEvent(100, 5); - appendMotionEvent(200, 10); + appendMoveEvent(0, 0); + appendMoveEvent(100, 5); + appendMoveEvent(200, 10); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(100, 0); - appendMotionEvent(200, 10); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(100, 0); + appendMoveEvent(200, 10); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(100, -10); - appendMotionEvent(200, 10); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(100, -10); + appendMoveEvent(200, 10); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(100, -10); - appendMotionEvent(200, 50); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(100, -10); + appendMoveEvent(200, 50); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void test_between45And90() { - appendMotionEvent(0, 0); - appendMotionEvent(10, 50); - appendMotionEvent(8, 100); + appendMoveEvent(0, 0); + appendMoveEvent(10, 50); + appendMoveEvent(8, 100); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(1, 800); - appendMotionEvent(2, 900); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(1, 800); + appendMoveEvent(2, 900); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-10, 600); - appendMotionEvent(30, 700); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-10, 600); + appendMoveEvent(30, 700); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(40, 100); - appendMotionEvent(0, 101); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(40, 100); + appendMoveEvent(0, 101); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void test_between90And135() { - appendMotionEvent(0, 0); - appendMotionEvent(-10, 50); - appendMotionEvent(-24, 100); + appendMoveEvent(0, 0); + appendMoveEvent(-10, 50); + appendMoveEvent(-24, 100); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-20, 800); - appendMotionEvent(-20, 900); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-20, 800); + appendMoveEvent(-20, 900); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(30, 600); - appendMotionEvent(-10, 700); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(30, 600); + appendMoveEvent(-10, 700); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-80, 100); - appendMotionEvent(-10, 101); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-80, 100); + appendMoveEvent(-10, 101); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void test_between135And180() { - appendMotionEvent(0, 0); - appendMotionEvent(-120, 10); - appendMotionEvent(-200, 20); + appendMoveEvent(0, 0); + appendMoveEvent(-120, 10); + appendMoveEvent(-200, 20); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-20, 8); - appendMotionEvent(-40, 2); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-20, 8); + appendMoveEvent(-40, 2); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-500, -2); - appendMotionEvent(-600, 70); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-500, -2); + appendMoveEvent(-600, 70); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-80, 100); - appendMotionEvent(-100, 1); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-80, 100); + appendMoveEvent(-100, 1); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void test_between180And225() { - appendMotionEvent(0, 0); - appendMotionEvent(-120, -10); - appendMotionEvent(-200, -20); + appendMoveEvent(0, 0); + appendMoveEvent(-120, -10); + appendMoveEvent(-200, -20); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-20, -8); - appendMotionEvent(-40, -2); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-20, -8); + appendMoveEvent(-40, -2); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-500, 2); - appendMotionEvent(-600, -70); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-500, 2); + appendMoveEvent(-600, -70); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-80, -100); - appendMotionEvent(-100, -1); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-80, -100); + appendMoveEvent(-100, -1); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void test_between225And270() { - appendMotionEvent(0, 0); - appendMotionEvent(-12, -20); - appendMotionEvent(-20, -40); + appendMoveEvent(0, 0); + appendMoveEvent(-12, -20); + appendMoveEvent(-20, -40); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-20, -130); - appendMotionEvent(-40, -260); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-20, -130); + appendMoveEvent(-40, -260); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(1, -100); - appendMotionEvent(-6, -200); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(1, -100); + appendMoveEvent(-6, -200); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-80, -100); - appendMotionEvent(-10, -110); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-80, -100); + appendMoveEvent(-10, -110); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void test_between270And315() { - appendMotionEvent(0, 0); - appendMotionEvent(12, -20); - appendMotionEvent(20, -40); + appendMoveEvent(0, 0); + appendMoveEvent(12, -20); + appendMoveEvent(20, -40); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(20, -130); - appendMotionEvent(40, -260); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(20, -130); + appendMoveEvent(40, -260); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(-1, -100); - appendMotionEvent(6, -200); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(-1, -100); + appendMoveEvent(6, -200); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(80, -100); - appendMotionEvent(10, -110); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(80, -100); + appendMoveEvent(10, -110); assertThat(mClassifier.isFalseTouch(), is(true)); } @Test public void test_between315And360() { - appendMotionEvent(0, 0); - appendMotionEvent(120, -20); - appendMotionEvent(200, -40); + appendMoveEvent(0, 0); + appendMoveEvent(120, -20); + appendMoveEvent(200, -40); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(200, -13); - appendMotionEvent(400, -30); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(200, -13); + appendMoveEvent(400, -30); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(100, 10); - appendMotionEvent(600, -20); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(100, 10); + appendMoveEvent(600, -20); assertThat(mClassifier.isFalseTouch(), is(false)); - mMotionEvents.clear(); - appendMotionEvent(0, 0); - appendMotionEvent(80, -100); - appendMotionEvent(100, -1); + resetDataProvider(); + appendMoveEvent(0, 0); + appendMoveEvent(80, -100); + appendMoveEvent(100, -1); assertThat(mClassifier.isFalseTouch(), is(true)); } @@ -397,74 +362,50 @@ public class ZigZagClassifierTest extends SysuiTestCase { // We use a pre-determined seed to make this test repeatable. Random rand = new Random(23); for (int i = 0; i < 100; i++) { - mOffsetX = rand.nextInt(2000) - 1000; - mOffsetY = rand.nextInt(2000) - 1000; + setOffsetX(rand.nextInt(2000) - 1000); + setOffsetY(rand.nextInt(2000) - 1000); try { - clearMotionEvents(); + resetDataProvider(); testPass_fewTouchesVertical(); - clearMotionEvents(); + resetDataProvider(); testPass_vertical(); - clearMotionEvents(); + resetDataProvider(); testFail_horizontalStraightVerticalZigZag(); - clearMotionEvents(); + resetDataProvider(); testFail_horizontalZigZagVerticalStraight(); - clearMotionEvents(); + resetDataProvider(); testFail_minimumTouchesHorizontal(); - clearMotionEvents(); + resetDataProvider(); testFail_minimumTouchesVertical(); - clearMotionEvents(); + resetDataProvider(); testPass_fewTouchesHorizontal(); - clearMotionEvents(); + resetDataProvider(); testPass_fortyFiveDegreesStraight(); - clearMotionEvents(); + resetDataProvider(); testPass_horizontal(); - clearMotionEvents(); + resetDataProvider(); testPass_horizontalStraightVerticalZigZag(); - clearMotionEvents(); + resetDataProvider(); testPass_horizontalZigZagVerticalStraight(); - clearMotionEvents(); + resetDataProvider(); test_between0And45(); - clearMotionEvents(); + resetDataProvider(); test_between45And90(); - clearMotionEvents(); + resetDataProvider(); test_between90And135(); - clearMotionEvents(); + resetDataProvider(); test_between135And180(); - clearMotionEvents(); + resetDataProvider(); test_between180And225(); - clearMotionEvents(); + resetDataProvider(); test_between225And270(); - clearMotionEvents(); + resetDataProvider(); test_between270And315(); - clearMotionEvents(); + resetDataProvider(); test_between315And360(); } catch (AssertionError e) { throw new AssertionError("Random origin failure in iteration " + i, e); } } } - - private void clearMotionEvents() { - for (MotionEvent motionEvent : mMotionEvents) { - motionEvent.recycle(); - } - mMotionEvents.clear(); - } - - private void appendMotionEvent(float x, float y) { - x += mOffsetX; - y += mOffsetY; - - long eventTime = mMotionEvents.size() + 1; - MotionEvent motionEvent = MotionEvent.obtain(1, eventTime, MotionEvent.ACTION_DOWN, x, y, - 0); - mMotionEvents.add(motionEvent); - - mDx = mDataProvider.getFirstRecentMotionEvent().getX() - - mDataProvider.getLastMotionEvent().getX(); - mDy = mDataProvider.getFirstRecentMotionEvent().getY() - - mDataProvider.getLastMotionEvent().getY(); - - mClassifier.onTouchEvent(motionEvent); - } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e81d1721b271..fc67c38560fa 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2616,9 +2616,11 @@ public class ConnectivityService extends IConnectivityManager.Stub final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0); final boolean wasValidated = nai.lastValidated; final boolean wasDefault = isDefaultNetwork(nai); - if (nai.everCaptivePortalDetected && !nai.captivePortalLoginNotified - && valid) { - nai.captivePortalLoginNotified = true; + // Only show a connected notification if the network is pending validation + // after the captive portal app was open, and it has now validated. + if (nai.captivePortalValidationPending && valid) { + // User is now logged in, network validated. + nai.captivePortalValidationPending = false; showNetworkNotification(nai, NotificationType.LOGGED_IN); } @@ -2689,9 +2691,6 @@ public class ConnectivityService extends IConnectivityManager.Stub final int oldScore = nai.getCurrentScore(); nai.lastCaptivePortalDetected = visible; nai.everCaptivePortalDetected |= visible; - if (visible) { - nai.captivePortalLoginNotified = false; - } if (nai.lastCaptivePortalDetected && Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) { if (DBG) log("Avoiding captive portal network: " + nai.name()); @@ -3500,6 +3499,12 @@ public class ConnectivityService extends IConnectivityManager.Stub new CaptivePortal(new CaptivePortalImpl(network).asBinder())); appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); + // This runs on a random binder thread, but getNetworkAgentInfoForNetwork is thread-safe, + // and captivePortalValidationPending is volatile. + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); + if (nai != null) { + nai.captivePortalValidationPending = true; + } Binder.withCleanCallingIdentity(() -> mContext.startActivityAsUser(appIntent, UserHandle.CURRENT)); } diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 7ab70fad70d4..ed64475084b8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -42,7 +42,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_ALL = false; // Available log categories in the activity manager package. - static final boolean DEBUG_ANR = true; // STOPSHIP disable it (b/113252928) + static final boolean DEBUG_ANR = false; static final boolean DEBUG_BACKGROUND_CHECK = DEBUG_ALL || false; static final boolean DEBUG_BACKUP = DEBUG_ALL || false; static final boolean DEBUG_BROADCAST = DEBUG_ALL || false; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b342d37dd37a..91ae156afdf5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -272,6 +272,7 @@ import android.os.storage.IStorageManager; import android.os.storage.StorageManager; import android.provider.DeviceConfig; import android.provider.Settings; +import android.provider.DeviceConfig.Properties; import android.server.ServerProtoEnums; import android.sysprop.VoldProperties; import android.text.TextUtils; @@ -356,6 +357,7 @@ import com.android.server.uri.GrantUri; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.utils.PriorityDump; import com.android.server.vr.VrManagerInternal; +import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityServiceConnectionsHolder; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerService; @@ -392,6 +394,7 @@ import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiFunction; @@ -861,6 +864,51 @@ public class ActivityManagerService extends IActivityManager.Stub */ final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>(); + /** + * Depth of overlapping activity-start PSS deferral notes + */ + private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0); + + private final ActivityMetricsLaunchObserver mActivityLaunchObserver = + new ActivityMetricsLaunchObserver() { + @Override + public void onActivityLaunched(byte[] activity, int temperature) { + // This is safe to force to the head of the queue because it relies only + // on refcounting to track begin/end of deferrals, not on actual + // message ordering. We don't care *what* activity is being + // launched; only that we're doing so. + if (mPssDeferralTime > 0) { + final Message msg = mBgHandler.obtainMessage(DEFER_PSS_MSG); + mBgHandler.sendMessageAtFrontOfQueue(msg); + } + } + + // The other observer methods are unused + @Override + public void onIntentStarted(Intent intent) { + } + + @Override + public void onIntentFailed() { + } + + @Override + public void onActivityLaunchCancelled(byte[] abortingActivity) { + } + + @Override + public void onActivityLaunchFinished(byte[] finalActivity) { + } + }; + + /** + * How long we defer PSS gathering while activities are starting, in milliseconds. + * This is adjustable via DeviceConfig. If it is zero or negative, no PSS deferral + * is done. + */ + private volatile long mPssDeferralTime = 0; + private static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer"; + private boolean mBinderTransactionTrackingEnabled = false; /** @@ -874,6 +922,20 @@ public class ActivityManagerService extends IActivityManager.Stub */ boolean mFullPssPending = false; + /** + * Observe DeviceConfig changes to the PSS calculation interval + */ + private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener = + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(Properties properties) { + mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0); + if (DEBUG_PSS) { + Slog.d(TAG_PSS, "Activity-start PSS delay now " + + mPssDeferralTime + " ms"); + } + } + }; /** * This is for verifying the UID report flow. @@ -1838,6 +1900,8 @@ public class ActivityManagerService extends IActivityManager.Stub } static final int COLLECT_PSS_BG_MSG = 1; + static final int DEFER_PSS_MSG = 2; + static final int STOP_DEFERRING_PSS_MSG = 3; final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) { @Override @@ -1945,6 +2009,30 @@ public class ActivityManagerService extends IActivityManager.Stub } } while (true); } + + case DEFER_PSS_MSG: { + deferPssForActivityStart(); + } break; + + case STOP_DEFERRING_PSS_MSG: { + final int nesting = mActivityStartingNesting.decrementAndGet(); + if (nesting <= 0) { + if (DEBUG_PSS) { + Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now " + + nesting); + } + if (nesting < 0) { + Slog.wtf(TAG, "Activity start nesting undercount!"); + mActivityStartingNesting.incrementAndGet(); + } + } else { + if (DEBUG_PSS) { + Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting); + } + } + } + break; + } } }; @@ -3694,9 +3782,7 @@ public class ActivityManagerService extends IActivityManager.Stub ArrayList<Integer> nativePids) { ArrayList<Integer> extraPids = null; - if (DEBUG_ANR) { - Slog.d(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids); - } + Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids); // Measure CPU usage as soon as we're called in order to get a realistic sampling // of the top users at the time of the request. @@ -3718,8 +3804,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid); extraPids.add(stats.pid); - } else if (DEBUG_ANR) { - Slog.d(TAG, "Skipping next CPU consuming process, not a java proc: " + } else { + Slog.i(TAG, "Skipping next CPU consuming process, not a java proc: " + stats.pid); } } @@ -3737,9 +3823,6 @@ public class ActivityManagerService extends IActivityManager.Stub if (tracesFile == null) { return null; } - if (DEBUG_ANR) { - Slog.d(TAG, "Dumping to " + tracesFile.getAbsolutePath()); - } dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids); return tracesFile; @@ -3832,6 +3915,8 @@ public class ActivityManagerService extends IActivityManager.Stub public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids, ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) { + Slog.i(TAG, "Dumping to " + tracesFile); + // We don't need any sort of inotify based monitoring when we're dumping traces via // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full // control of all writes to the file in question. @@ -3843,7 +3928,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (firstPids != null) { int num = firstPids.size(); for (int i = 0; i < num; i++) { - if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid " + firstPids.get(i)); + Slog.i(TAG, "Collecting stacks for pid " + firstPids.get(i)); final long timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, remainingTime); @@ -3863,7 +3948,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Next collect the stacks of the native pids if (nativePids != null) { for (int pid : nativePids) { - if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid); + Slog.i(TAG, "Collecting stacks for native pid " + pid); final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime); final long start = SystemClock.elapsedRealtime(); @@ -3887,7 +3972,7 @@ public class ActivityManagerService extends IActivityManager.Stub // Lastly, dump stacks for all extra PIDs from the CPU tracker. if (extraPids != null) { for (int pid : extraPids) { - if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid); + Slog.i(TAG, "Collecting stacks for extra pid " + pid); final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime); @@ -3903,6 +3988,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } } + Slog.i(TAG, "Done dumping"); } @Override @@ -8845,6 +8931,12 @@ public class ActivityManagerService extends IActivityManager.Stub NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS); mHiddenApiBlacklist.registerObserver(); + final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + ACTIVITY_START_PSS_DEFER_CONFIG, 0L); + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + ActivityThread.currentApplication().getMainExecutor(), + mPssDelayConfigListener); + synchronized (this) { mDebugApp = mOrigDebugApp = debugApp; mWaitForDebugger = mOrigWaitForDebugger = waitForDebugger; @@ -8861,6 +8953,7 @@ public class ActivityManagerService extends IActivityManager.Stub com.android.internal.R.bool.config_multiuserDelayUserDataLocking); mWaitForNetworkTimeoutMs = waitForNetworkTimeoutMs; + mPssDeferralTime = pssDeferralMs; } } @@ -8923,6 +9016,7 @@ public class ActivityManagerService extends IActivityManager.Stub EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY, SystemClock.uptimeMillis()); mAtmInternal.updateTopComponentForFactoryTest(); + mAtmInternal.getLaunchObserverRegistry().registerLaunchObserver(mActivityLaunchObserver); watchDeviceProvisioning(mContext); @@ -16146,7 +16240,13 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } if (mPendingPssProcesses.size() == 0) { - mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG); + final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0) + ? mPssDeferralTime : 0; + if (DEBUG_PSS && deferral > 0) { + Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by " + + deferral + " ms"); + } + mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, deferral); } if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc); proc.pssProcState = procState; @@ -16156,6 +16256,30 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Re-defer a posted PSS collection pass, if one exists. Assumes deferral is + * currently active policy when called. + */ + private void deferPssIfNeededLocked() { + if (mPendingPssProcesses.size() > 0) { + mBgHandler.removeMessages(COLLECT_PSS_BG_MSG); + mBgHandler.sendEmptyMessageDelayed(COLLECT_PSS_BG_MSG, mPssDeferralTime); + } + } + + private void deferPssForActivityStart() { + synchronized (ActivityManagerService.this) { + if (mPssDeferralTime > 0) { + if (DEBUG_PSS) { + Slog.d(TAG_PSS, "Deferring PSS collection for activity start"); + } + deferPssIfNeededLocked(); + mActivityStartingNesting.getAndIncrement(); + mBgHandler.sendEmptyMessageDelayed(STOP_DEFERRING_PSS_MSG, mPssDeferralTime); + } + } + } + + /** * Schedule PSS collection of all processes. */ void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 5ade50a5f248..bcfe71b82a6a 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -431,7 +431,7 @@ public final class OomAdjuster { for (int i = 0; i < N; i++) { ProcessRecord app = mProcessList.mLruProcesses.get(i); if (!app.killedByAm && app.thread != null && app.containsCycle == true) { - if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now, + if (computeOomAdjLocked(app, app.getCurRawAdj(), TOP_APP, true, now, true)) { retryCycles = true; } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 864a793b8f40..5b043799f848 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -155,9 +155,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // Whether a captive portal was found during the last network validation attempt. public boolean lastCaptivePortalDetected; - // Indicates the user was notified of a successful captive portal login since a portal was - // last detected. - public boolean captivePortalLoginNotified; + // Indicates the captive portal app was opened to show a login UI to the user, but the network + // has not validated yet. + public volatile boolean captivePortalValidationPending; // Set to true when partial connectivity was detected. public boolean partialConnectivity; @@ -629,7 +629,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " + "everCaptivePortalDetected{" + everCaptivePortalDetected + "} " + "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} " - + "captivePortalLoginNotified{" + captivePortalLoginNotified + "} " + + "captivePortalValidationPending{" + captivePortalValidationPending + "} " + "partialConnectivity{" + partialConnectivity + "} " + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} " + "clat{" + clatd + "} " diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 3a78aa2fbbf5..037293f9536c 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -50,6 +50,7 @@ import android.permission.PermissionControllerManager; import android.provider.Telephony; import android.telecom.TelecomManager; import android.util.ArraySet; +import android.util.LongSparseLongArray; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; @@ -58,6 +59,7 @@ import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; +import com.android.internal.util.IntPair; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; @@ -147,11 +149,9 @@ public final class PermissionPolicyService extends SystemService { PermissionInfo perm = dangerousPerms.get(i); if (perm.isHardRestricted() || perm.backgroundPermission != null) { - appOpsService.startWatchingMode(AppOpsManager.permissionToOpCode(perm.name), - null, appOpsListener); + appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener); } else if (perm.isSoftRestricted()) { - appOpsService.startWatchingMode(AppOpsManager.permissionToOpCode(perm.name), - null, appOpsListener); + appOpsService.startWatchingMode(getSwitchOp(perm.name), null, appOpsListener); SoftRestrictedPermissionPolicy policy = SoftRestrictedPermissionPolicy.forPermission(null, null, null, @@ -167,6 +167,25 @@ public final class PermissionPolicyService extends SystemService { } } + /** + * Get op that controls the access related to the permission. + * + * <p>Usually the permission-op relationship is 1:1 but some permissions (e.g. fine location) + * {@link AppOpsManager#sOpToSwitch share an op} to control the access. + * + * @param permission The permission + * + * @return The op that controls the access of the permission + */ + private static int getSwitchOp(@NonNull String permission) { + int op = AppOpsManager.permissionToOpCode(permission); + if (op == OP_NONE) { + return OP_NONE; + } + + return AppOpsManager.opToSwitch(op); + } + private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName, @UserIdInt int changedUserId) { if (isStarted(changedUserId)) { @@ -430,40 +449,89 @@ public final class PermissionPolicyService extends SystemService { * <p>This processes ops previously added by {@link #addOpIfRestricted} */ private void syncPackages() { + // Remember which ops were already set. This makes sure that we always set the most + // permissive mode if two OpChanges are scheduled. This can e.g. happen if two + // permissions change the same op. See {@link #getSwitchOp}. + LongSparseLongArray alreadySetAppOps = new LongSparseLongArray(); + final int allowCount = mOpsToAllow.size(); for (int i = 0; i < allowCount; i++) { final OpToChange op = mOpsToAllow.get(i); + setUidModeAllowed(op.code, op.uid, op.packageName); + alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1); } + final int allowIfDefaultCount = mOpsToAllowIfDefault.size(); for (int i = 0; i < allowIfDefaultCount; i++) { final OpToChange op = mOpsToAllowIfDefault.get(i); - setUidModeAllowedIfDefault(op.code, op.uid, op.packageName); + if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) { + continue; + } + + boolean wasSet = setUidModeAllowedIfDefault(op.code, op.uid, op.packageName); + if (wasSet) { + alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1); + } } - final int foregroundCount = mOpsToForegroundIfAllow.size(); - for (int i = 0; i < foregroundCount; i++) { + + final int foregroundIfAllowedCount = mOpsToForegroundIfAllow.size(); + for (int i = 0; i < foregroundIfAllowedCount; i++) { final OpToChange op = mOpsToForegroundIfAllow.get(i); - setUidModeForegroundIfAllow(op.code, op.uid, op.packageName); + if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) { + continue; + } + + boolean wasSet = setUidModeForegroundIfAllow(op.code, op.uid, op.packageName); + if (wasSet) { + alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1); + } } - final int foregroundIfAllowCount = mOpsToForeground.size(); - for (int i = 0; i < foregroundIfAllowCount; i++) { + + final int foregroundCount = mOpsToForeground.size(); + for (int i = 0; i < foregroundCount; i++) { final OpToChange op = mOpsToForeground.get(i); + if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) { + continue; + } + setUidModeForeground(op.code, op.uid, op.packageName); + alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1); } + final int ignoreCount = mOpsToIgnore.size(); for (int i = 0; i < ignoreCount; i++) { final OpToChange op = mOpsToIgnore.get(i); + if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) { + continue; + } + setUidModeIgnored(op.code, op.uid, op.packageName); + alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1); } + final int ignoreIfDefaultCount = mOpsToIgnoreIfDefault.size(); for (int i = 0; i < ignoreIfDefaultCount; i++) { final OpToChange op = mOpsToIgnoreIfDefault.get(i); - setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName); + if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) { + continue; + } + + boolean wasSet = setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName); + if (wasSet) { + alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1); + } } + final int defaultCount = mOpsToDefault.size(); for (int i = 0; i < defaultCount; i++) { final OpToChange op = mOpsToDefault.get(i); + if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) { + continue; + } + setUidModeDefault(op.code, op.uid, op.packageName); + alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1); } } @@ -479,7 +547,7 @@ public final class PermissionPolicyService extends SystemService { private void addOpIfRestricted(@NonNull PermissionInfo permissionInfo, @NonNull PackageInfo pkg) { final String permission = permissionInfo.name; - final int opCode = AppOpsManager.permissionToOpCode(permission); + final int opCode = getSwitchOp(permission); final int uid = pkg.applicationInfo.uid; if (!permissionInfo.isRestricted()) { @@ -581,7 +649,7 @@ public final class PermissionPolicyService extends SystemService { } final String permission = permissionInfo.name; - final int opCode = AppOpsManager.permissionToOpCode(permission); + final int opCode = getSwitchOp(permission); final String pkgName = pkg.packageName; final int uid = pkg.applicationInfo.uid; @@ -641,7 +709,7 @@ public final class PermissionPolicyService extends SystemService { } for (String permission : pkg.requestedPermissions) { - final int opCode = AppOpsManager.permissionToOpCode(permission); + final int opCode = getSwitchOp(permission); if (opCode == OP_NONE) { continue; } @@ -658,24 +726,27 @@ public final class PermissionPolicyService extends SystemService { } } - private void setUidModeAllowedIfDefault(int opCode, int uid, @NonNull String packageName) { - setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName); + private boolean setUidModeAllowedIfDefault(int opCode, int uid, + @NonNull String packageName) { + return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_ALLOWED, packageName); } private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) { setUidMode(opCode, uid, MODE_ALLOWED, packageName); } - private void setUidModeForegroundIfAllow(int opCode, int uid, @NonNull String packageName) { - setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName); + private boolean setUidModeForegroundIfAllow(int opCode, int uid, + @NonNull String packageName) { + return setUidModeIfMode(opCode, uid, MODE_ALLOWED, MODE_FOREGROUND, packageName); } private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) { setUidMode(opCode, uid, MODE_FOREGROUND, packageName); } - private void setUidModeIgnoredIfDefault(int opCode, int uid, @NonNull String packageName) { - setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName); + private boolean setUidModeIgnoredIfDefault(int opCode, int uid, + @NonNull String packageName) { + return setUidModeIfMode(opCode, uid, MODE_DEFAULT, MODE_IGNORED, packageName); } private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) { @@ -692,14 +763,17 @@ public final class PermissionPolicyService extends SystemService { } } - private void setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode, + private boolean setUidModeIfMode(int opCode, int uid, int requiredModeBefore, int newMode, @NonNull String packageName) { final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager .opToPublicName(opCode), uid, packageName); if (currentMode == requiredModeBefore) { mAppOpsManager.setUidMode(opCode, uid, newMode); + return true; } + + return false; } private void setUidModeDefault(int opCode, int uid, String packageName) { diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING index c7f8c07432a8..c7e241b35e9a 100644 --- a/services/core/java/com/android/server/policy/TEST_MAPPING +++ b/services/core/java/com/android/server/policy/TEST_MAPPING @@ -41,6 +41,9 @@ "options": [ { "include-filter": "android.permission.cts.SplitPermissionTest" + }, + { + "include-filter": "android.permission.cts.BackgroundPermissionsTest" } ] } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index bbfb3c555657..cf87203893cf 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -478,8 +478,10 @@ public class DisplayPolicy { @Override public void onSwipeFromRight() { - final Region excludedRegion = - mDisplayContent.calculateSystemGestureExclusion(); + final Region excludedRegion; + synchronized (mLock) { + excludedRegion = mDisplayContent.calculateSystemGestureExclusion(); + } final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture || mNavigationBarPosition == NAV_BAR_RIGHT; if (mNavigationBar != null && sideAllowed @@ -490,8 +492,10 @@ public class DisplayPolicy { @Override public void onSwipeFromLeft() { - final Region excludedRegion = - mDisplayContent.calculateSystemGestureExclusion(); + final Region excludedRegion; + synchronized (mLock) { + excludedRegion = mDisplayContent.calculateSystemGestureExclusion(); + } final boolean sideAllowed = mNavigationBarAlwaysShowOnSideGesture || mNavigationBarPosition == NAV_BAR_LEFT; if (mNavigationBar != null && sideAllowed diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index ba2325877075..af729317392b 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -25,6 +25,7 @@ import static com.android.server.wm.PinnedStackControllerProto.MOVEMENT_BOUNDS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.NonNull; import android.app.RemoteAction; import android.content.pm.ParceledListSlice; import android.content.res.Resources; @@ -45,6 +46,7 @@ import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import com.android.internal.policy.PipSnapAlgorithm; +import com.android.internal.util.Preconditions; import com.android.server.UiThread; import java.io.PrintWriter; @@ -326,8 +328,8 @@ class PinnedStackController { boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) { synchronized (mService.mGlobalLock) { final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); - if (mDisplayInfo.equals(displayInfo)) { - // We are already in the right orientation, ignore + if (isSameDimensionAndRotation(mDisplayInfo, displayInfo)) { + // No dimension/rotation change, ignore outBounds.setEmpty(); return false; } else if (targetBounds.isEmpty()) { @@ -427,6 +429,15 @@ class PinnedStackController { notifyActionsChanged(mActions); } + private boolean isSameDimensionAndRotation(@NonNull DisplayInfo display1, + @NonNull DisplayInfo display2) { + Preconditions.checkNotNull(display1); + Preconditions.checkNotNull(display2); + return ((display1.rotation == display2.rotation) + && (display1.logicalWidth == display2.logicalWidth) + && (display1.logicalHeight == display2.logicalHeight)); + } + /** * Notifies listeners that the PIP needs to be adjusted for the IME. */ diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 481c3ba24fca..114a56feaf73 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1936,8 +1936,12 @@ public class TaskStack extends WindowContainer<Task> implements public boolean setPinnedStackAlpha(float alpha) { // Hold the lock since this is called from the BoundsAnimator running on the UiThread synchronized (mWmService.mGlobalLock) { - getPendingTransaction().setAlpha(getSurfaceControl(), - mCancelCurrentBoundsAnimation ? 1 : alpha); + final SurfaceControl sc = getSurfaceControl(); + if (sc == null || !sc.isValid()) { + // If the stack is already removed, don't bother updating any stack animation + return false; + } + getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha); scheduleAnimation(); return !mCancelCurrentBoundsAnimation; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 57fa2ed63121..28bb3d8999c3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7690,7 +7690,7 @@ public class WindowManagerService extends IWindowManager.Stub private void onPointerDownOutsideFocusLocked(IBinder touchedToken) { final WindowState touchedWindow = windowForClientLocked(null, touchedToken, false); - if (touchedWindow == null) { + if (touchedWindow == null || !touchedWindow.canReceiveKeys()) { return; } diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java index 2f1505cd9e4e..5e4818a67b31 100644 --- a/telecomm/java/android/telecom/ConferenceParticipant.java +++ b/telecomm/java/android/telecom/ConferenceParticipant.java @@ -19,6 +19,7 @@ package android.telecom; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; @@ -69,18 +70,28 @@ public class ConferenceParticipant implements Parcelable { private long mConnectElapsedTime; /** + * The direction of the call; + * {@link Call.Details#DIRECTION_INCOMING} for incoming calls, or + * {@link Call.Details#DIRECTION_OUTGOING} for outgoing calls. + */ + private int mCallDirection; + + /** * Creates an instance of {@code ConferenceParticipant}. * * @param handle The conference participant's handle (e.g., phone number). * @param displayName The display name for the participant. * @param endpoint The enpoint Uri which uniquely identifies this conference participant. * @param state The state of the participant in the conference. + * @param callDirection The direction of the call (incoming/outgoing). */ - public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state) { + public ConferenceParticipant(Uri handle, String displayName, Uri endpoint, int state, + int callDirection) { mHandle = handle; mDisplayName = displayName; mEndpoint = endpoint; mState = state; + mCallDirection = callDirection; } /** @@ -96,7 +107,16 @@ public class ConferenceParticipant implements Parcelable { String displayName = source.readString(); Uri endpoint = source.readParcelable(classLoader); int state = source.readInt(); - return new ConferenceParticipant(handle, displayName, endpoint, state); + long connectTime = source.readLong(); + long elapsedRealTime = source.readLong(); + int callDirection = source.readInt(); + ConferenceParticipant participant = + new ConferenceParticipant(handle, displayName, endpoint, state, + callDirection); + participant.setConnectTime(connectTime); + participant.setConnectElapsedTime(elapsedRealTime); + participant.setCallDirection(callDirection); + return participant; } @Override @@ -170,6 +190,9 @@ public class ConferenceParticipant implements Parcelable { dest.writeString(mDisplayName); dest.writeParcelable(mEndpoint, 0); dest.writeInt(mState); + dest.writeLong(mConnectTime); + dest.writeLong(mConnectElapsedTime); + dest.writeInt(mCallDirection); } /** @@ -192,6 +215,8 @@ public class ConferenceParticipant implements Parcelable { sb.append(getConnectTime()); sb.append(" ConnectElapsedTime: "); sb.append(getConnectElapsedTime()); + sb.append(" Direction: "); + sb.append(getCallDirection() == Call.Details.DIRECTION_INCOMING ? "Incoming" : "Outgoing"); sb.append("]"); return sb.toString(); } @@ -239,7 +264,7 @@ public class ConferenceParticipant implements Parcelable { } /** - * The connect elpased time of the participant to the conference. + * The connect elapsed time of the participant to the conference. */ public long getConnectElapsedTime() { return mConnectElapsedTime; @@ -248,4 +273,76 @@ public class ConferenceParticipant implements Parcelable { public void setConnectElapsedTime(long connectElapsedTime) { mConnectElapsedTime = connectElapsedTime; } + + /** + * @return The direction of the call (incoming/outgoing). + */ + public @Call.Details.CallDirection int getCallDirection() { + return mCallDirection; + } + + /** + * Sets the direction of the call. + * @param callDirection Whether the call is incoming or outgoing. + */ + public void setCallDirection(@Call.Details.CallDirection int callDirection) { + mCallDirection = callDirection; + } + + /** + * Attempts to build a tel: style URI from a conference participant. + * Conference event package data contains SIP URIs, so we try to extract the phone number and + * format into a typical tel: style URI. + * + * @param address The conference participant's address. + * @param countryIso The country ISO of the current subscription; used when formatting the + * participant phone number to E.164 format. + * @return The participant's address URI. + * @hide + */ + @VisibleForTesting + public static Uri getParticipantAddress(Uri address, String countryIso) { + if (address == null) { + return address; + } + // Even if address is already in tel: format, still parse it and rebuild. + // This is to recognize tel URIs such as: + // tel:6505551212;phone-context=ims.mnc012.mcc034.3gppnetwork.org + + // Conference event package participants are identified using SIP URIs (see RFC3261). + // A valid SIP uri has the format: sip:user:password@host:port;uri-parameters?headers + // Per RFC3261, the "user" can be a telephone number. + // For example: sip:1650555121;phone-context=blah.com@host.com + // In this case, the phone number is in the user field of the URI, and the parameters can be + // ignored. + // + // A SIP URI can also specify a phone number in a format similar to: + // sip:+1-212-555-1212@something.com;user=phone + // In this case, the phone number is again in user field and the parameters can be ignored. + // We can get the user field in these instances by splitting the string on the @, ;, or : + // and looking at the first found item. + String number = address.getSchemeSpecificPart(); + if (TextUtils.isEmpty(number)) { + return address; + } + + String numberParts[] = number.split("[@;:]"); + if (numberParts.length == 0) { + return address; + } + number = numberParts[0]; + + // Attempt to format the number in E.164 format and use that as part of the TEL URI. + // RFC2806 recommends to format telephone numbers using E.164 since it is independent of + // how the dialing of said numbers takes place. + // If conversion to E.164 fails, the returned value is null. In that case, fallback to the + // number which was in the CEP data. + String formattedNumber = null; + if (!TextUtils.isEmpty(countryIso)) { + formattedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso); + } + + return Uri.fromParts(PhoneAccount.SCHEME_TEL, + formattedNumber != null ? formattedNumber : number, null); + } } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 47587c5787f4..0983eea8e819 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -1793,6 +1793,11 @@ public abstract class Connection extends Conferenceable { private ConnectionService mConnectionService; private Bundle mExtras; private final Object mExtrasLock = new Object(); + /** + * The direction of the connection; used where an existing connection is created and we need to + * communicate to Telecom whether its incoming or outgoing. + */ + private @Call.Details.CallDirection int mCallDirection = Call.Details.DIRECTION_UNKNOWN; /** * Tracks the key set for the extras bundle provided on the last invocation of @@ -3357,4 +3362,21 @@ public abstract class Connection extends Conferenceable { l.onConnectionEvent(this, event, extras); } } + + /** + * @return The direction of the call. + * @hide + */ + public final @Call.Details.CallDirection int getCallDirection() { + return mCallDirection; + } + + /** + * Sets the direction of this connection. + * @param callDirection The direction of this connection. + * @hide + */ + public void setCallDirection(@Call.Details.CallDirection int callDirection) { + mCallDirection = callDirection; + } } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index 626fcc4ddc1e..35488100fb58 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -2144,7 +2144,8 @@ public abstract class ConnectionService extends Service { connection.getDisconnectCause(), emptyList, connection.getExtras(), - conferenceId); + conferenceId, + connection.getCallDirection()); mAdapter.addExistingConnection(id, parcelableConnection); } } diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java index dab1c6e7ae77..4734af6371d4 100644 --- a/telecomm/java/android/telecom/ParcelableConnection.java +++ b/telecomm/java/android/telecom/ParcelableConnection.java @@ -53,6 +53,7 @@ public final class ParcelableConnection implements Parcelable { private final List<String> mConferenceableConnectionIds; private final Bundle mExtras; private String mParentCallId; + private @Call.Details.CallDirection int mCallDirection; /** @hide */ public ParcelableConnection( @@ -75,13 +76,15 @@ public final class ParcelableConnection implements Parcelable { DisconnectCause disconnectCause, List<String> conferenceableConnectionIds, Bundle extras, - String parentCallId) { + String parentCallId, + @Call.Details.CallDirection int callDirection) { this(phoneAccount, state, capabilities, properties, supportedAudioRoutes, address, addressPresentation, callerDisplayName, callerDisplayNamePresentation, videoProvider, videoState, ringbackRequested, isVoipAudioMode, connectTimeMillis, connectElapsedTimeMillis, statusHints, disconnectCause, conferenceableConnectionIds, extras); mParentCallId = parentCallId; + mCallDirection = callDirection; } /** @hide */ @@ -125,6 +128,7 @@ public final class ParcelableConnection implements Parcelable { mConferenceableConnectionIds = conferenceableConnectionIds; mExtras = extras; mParentCallId = null; + mCallDirection = Call.Details.DIRECTION_UNKNOWN; } public PhoneAccountHandle getPhoneAccount() { @@ -219,6 +223,10 @@ public final class ParcelableConnection implements Parcelable { return mParentCallId; } + public @Call.Details.CallDirection int getCallDirection() { + return mCallDirection; + } + @Override public String toString() { return new StringBuilder() @@ -234,6 +242,8 @@ public final class ParcelableConnection implements Parcelable { .append(mExtras) .append(", parent:") .append(mParentCallId) + .append(", callDirection:") + .append(mCallDirection) .toString(); } @@ -265,6 +275,7 @@ public final class ParcelableConnection implements Parcelable { int supportedAudioRoutes = source.readInt(); String parentCallId = source.readString(); long connectElapsedTimeMillis = source.readLong(); + int callDirection = source.readInt(); return new ParcelableConnection( phoneAccount, @@ -286,7 +297,8 @@ public final class ParcelableConnection implements Parcelable { disconnectCause, conferenceableConnectionIds, extras, - parentCallId); + parentCallId, + callDirection); } @Override @@ -325,5 +337,6 @@ public final class ParcelableConnection implements Parcelable { destination.writeInt(mSupportedAudioRoutes); destination.writeString(mParentCallId); destination.writeLong(mConnectElapsedTimeMillis); + destination.writeInt(mCallDirection); } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 8c024a5a53c7..73ee7f544b5f 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -18,6 +18,7 @@ package com.android.server; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; +import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; @@ -80,13 +81,13 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -141,6 +142,7 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkMisc; import android.net.NetworkRequest; import android.net.NetworkSpecifier; +import android.net.NetworkStack; import android.net.NetworkStackClient; import android.net.NetworkState; import android.net.NetworkUtils; @@ -154,6 +156,7 @@ import android.net.shared.NetworkMonitorUtils; import android.net.shared.PrivateDnsConfig; import android.net.util.MultinetworkPolicyTracker; import android.os.Binder; +import android.os.Bundle; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; @@ -191,6 +194,7 @@ import com.android.server.connectivity.DefaultNetworkMetrics; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.Nat464Xlat; +import com.android.server.connectivity.NetworkNotificationManager.NotificationType; import com.android.server.connectivity.ProxyTracker; import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; @@ -282,6 +286,7 @@ public class ConnectivityServiceTest { @Mock NetworkStackClient mNetworkStack; @Mock PackageManager mPackageManager; @Mock UserManager mUserManager; + @Mock NotificationManager mNotificationManager; private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -351,7 +356,7 @@ public class ConnectivityServiceTest { @Override public Object getSystemService(String name) { if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm; - if (Context.NOTIFICATION_SERVICE.equals(name)) return mock(NotificationManager.class); + if (Context.NOTIFICATION_SERVICE.equals(name)) return mNotificationManager; if (Context.NETWORK_STACK_SERVICE.equals(name)) return mNetworkStack; if (Context.USER_SERVICE.equals(name)) return mUserManager; return super.getSystemService(name); @@ -371,7 +376,17 @@ public class ConnectivityServiceTest { public PackageManager getPackageManager() { return mPackageManager; } - } + + @Override + public void enforceCallingOrSelfPermission(String permission, String message) { + // The mainline permission can only be held if signed with the network stack certificate + // Skip testing for this permission. + if (NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK.equals(permission)) return; + // All other permissions should be held by the test or unnecessary: check as normal to + // make sure the code does not rely on unexpected permissions. + super.enforceCallingOrSelfPermission(permission, message); + } + } public void waitForIdle(int timeoutMsAsInt) { long timeoutMs = timeoutMsAsInt; @@ -2856,6 +2871,9 @@ public class ConnectivityServiceTest { // Expect NET_CAPABILITY_VALIDATED onAvailable callback. validatedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); + // Expect no notification to be shown when captive portal disappears by itself + verify(mNotificationManager, never()).notifyAsUser( + anyString(), eq(NotificationType.LOGGED_IN.eventId), any(), any()); // Break network connectivity. // Expect NET_CAPABILITY_VALIDATED onLost callback. @@ -2885,6 +2903,8 @@ public class ConnectivityServiceTest { // Check that calling startCaptivePortalApp does nothing. final int fastTimeoutMs = 100; mCm.startCaptivePortalApp(wifiNetwork); + waitForIdle(); + verify(mWiFiNetworkAgent.mNetworkMonitor, never()).launchCaptivePortalApp(); mServiceContext.expectNoStartActivityIntent(fastTimeoutMs); // Turn into a captive portal. @@ -2895,14 +2915,26 @@ public class ConnectivityServiceTest { // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. mCm.startCaptivePortalApp(wifiNetwork); - verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1)) - .launchCaptivePortalApp(); + waitForIdle(); + verify(mWiFiNetworkAgent.mNetworkMonitor).launchCaptivePortalApp(); + + // NetworkMonitor uses startCaptivePortal(Network, Bundle) (startCaptivePortalAppInternal) + final Bundle testBundle = new Bundle(); + final String testKey = "testkey"; + final String testValue = "testvalue"; + testBundle.putString(testKey, testValue); + mCm.startCaptivePortalApp(wifiNetwork, testBundle); + final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS); + assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction()); + assertEquals(testValue, signInIntent.getStringExtra(testKey)); // Report that the captive portal is dismissed, and check that callbacks are fired mWiFiNetworkAgent.setNetworkValid(); mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid()); validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + verify(mNotificationManager, times(1)).notifyAsUser(anyString(), + eq(NotificationType.LOGGED_IN.eventId), any(), eq(UserHandle.ALL)); mCm.unregisterNetworkCallback(validatedCallback); mCm.unregisterNetworkCallback(captivePortalCallback); diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java index daea601e9f96..0511f2411647 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java @@ -31,11 +31,8 @@ import java.util.Arrays; import java.util.Objects; /** - * Network specifier object used to request a Wi-Fi Aware network. Apps do not create these objects - * directly but obtain them using - * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} or - * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or their secure (Passphrase) - * versions. + * Network specifier object used to request a Wi-Fi Aware network. Apps should use the + * {@link WifiAwareNetworkSpecifier.Builder} class to create an instance. */ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable { /** |