Merge "Make ApiLint's new_since arg the merged txt file" into sc-dev
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index fc9e5e2..f9cdbd3 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -517,6 +517,25 @@
}
/**
+ * Unregisters the IAccessibilityManagerClient from the backing service
+ * @hide
+ */
+ public boolean removeClient() {
+ synchronized (mLock) {
+ IAccessibilityManager service = getServiceLocked();
+ if (service == null) {
+ return false;
+ }
+ try {
+ return service.removeClient(mClient, mUserId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+ }
+ }
+ return false;
+ }
+
+ /**
* @hide
*/
@VisibleForTesting
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index c71ea53..078ab25 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -42,6 +42,8 @@
long addClient(IAccessibilityManagerClient client, int userId);
+ boolean removeClient(IAccessibilityManagerClient client, int userId);
+
List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5e2209e..721260e 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4575,7 +4575,7 @@
int motionPosition = findMotionRow(y);
if (isGlowActive()) {
// Pressed during edge effect, so this is considered the same as a fling catch.
- mTouchMode = TOUCH_MODE_FLING;
+ touchMode = mTouchMode = TOUCH_MODE_FLING;
} else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {
// User clicked on an actual view (and was not stopping a fling).
// Remember where the motion event started
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 115c266..212fdca 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -116,6 +116,13 @@
}
@Test
+ public void testRemoveManager() throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+ manager.removeClient();
+ verify(mMockService).removeClient(manager.getClient(), UserHandle.USER_CURRENT);
+ }
+
+ @Test
public void testGetAccessibilityServiceList() throws Exception {
// create a list of installed accessibility services the mock service returns
List<AccessibilityServiceInfo> expectedServices = new ArrayList<>();
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
deleted file mode 100644
index 2104be4..0000000
--- a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- ~ Copyright (C) 2020 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.
- -->
-<!--
- The transparent circle outline that encircles the bubbles when they're in the dismiss target.
--->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
-
- <stroke
- android:width="1dp"
- android:color="#66FFFFFF" />
-
- <solid android:color="#B3000000" />
-</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml
deleted file mode 100644
index ff8fede..0000000
--- a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
- ~ Copyright (C) 2020 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.
- -->
-<!-- The 'X' bubble dismiss icon. This is just ic_close with a stroke. -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24.0dp"
- android:height="24.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
- android:fillColor="#FFFFFFFF"
- android:strokeColor="#FF000000"/>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml b/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
index 7809c83..f7fda36 100644
--- a/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
@@ -20,9 +20,8 @@
android:shape="oval">
<stroke
- android:width="1dp"
- android:color="#AAFFFFFF" />
+ android:width="2dp"
+ android:color="@android:color/system_accent1_600" />
- <solid android:color="#77000000" />
-
+ <solid android:color="@android:color/system_accent1_600" />
</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
index 6045626..62285e6 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
@@ -21,5 +21,5 @@
android:viewportHeight="24.0">
<path
android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
- android:fillColor="#FFFFFFFF"/>
+ android:fillColor="@android:color/system_neutral1_50"/>
</vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml b/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml
deleted file mode 100644
index f5cd727..0000000
--- a/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<!--
- ~ 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
- -->
-<!-- Bubble dismiss target consisting of an X icon and the text 'Dismiss'. -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/floating_dismiss_gradient_height"
- android:layout_gravity="bottom|center_horizontal">
-
- <FrameLayout
- android:id="@+id/bubble_dismiss_circle"
- android:layout_width="@dimen/bubble_dismiss_encircle_size"
- android:layout_height="@dimen/bubble_dismiss_encircle_size"
- android:layout_gravity="center"
- android:background="@drawable/bubble_dismiss_circle" />
-
- <LinearLayout
- android:id="@+id/bubble_dismiss_icon_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center"
- android:paddingBottom="@dimen/bubble_dismiss_target_padding_y"
- android:paddingTop="@dimen/bubble_dismiss_target_padding_y"
- android:paddingLeft="@dimen/bubble_dismiss_target_padding_x"
- android:paddingRight="@dimen/bubble_dismiss_target_padding_x"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/bubble_dismiss_close_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:src="@drawable/bubble_dismiss_icon" />
- </LinearLayout>
-</FrameLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 3caff35..dddf2c1 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -15,7 +15,8 @@
limitations under the License.
-->
<resources>
- <dimen name="dismiss_circle_size">52dp</dimen>
+ <dimen name="dismiss_circle_size">96dp</dimen>
+ <dimen name="dismiss_circle_small">60dp</dimen>
<!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
<dimen name="floating_dismiss_gradient_height">250dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 7e48a7e..d821c6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -34,10 +34,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
import android.graphics.Outline;
-import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -192,8 +189,7 @@
private final BubbleController mBubbleController;
private final BubbleData mBubbleData;
- private final ValueAnimator mDesaturateAndDarkenAnimator;
- private final Paint mDesaturateAndDarkenPaint = new Paint();
+ private final ValueAnimator mDismissBubbleAnimator;
private PhysicsAnimationLayout mBubbleContainer;
private StackAnimationController mStackAnimationController;
@@ -330,8 +326,8 @@
private boolean mIsExpansionAnimating = false;
private boolean mIsBubbleSwitchAnimating = false;
- /** The view to desaturate/darken when magneted to the dismiss target. */
- @Nullable private View mDesaturateAndDarkenTargetView;
+ /** The view to shrink and apply alpha to when magneted to the dismiss target. */
+ @Nullable private View mViewBeingDismissed;
private Rect mTempRect = new Rect();
@@ -415,8 +411,7 @@
if (mExpandedAnimationController.getDraggedOutBubble() == null) {
return;
}
-
- animateDesaturateAndDarken(
+ animateDismissBubble(
mExpandedAnimationController.getDraggedOutBubble(), true);
}
@@ -426,8 +421,7 @@
if (mExpandedAnimationController.getDraggedOutBubble() == null) {
return;
}
-
- animateDesaturateAndDarken(
+ animateDismissBubble(
mExpandedAnimationController.getDraggedOutBubble(), false);
if (wasFlungOut) {
@@ -459,14 +453,13 @@
@Override
public void onStuckToTarget(
@NonNull MagnetizedObject.MagneticTarget target) {
- animateDesaturateAndDarken(mBubbleContainer, true);
+ animateDismissBubble(mBubbleContainer, true);
}
@Override
public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
float velX, float velY, boolean wasFlungOut) {
- animateDesaturateAndDarken(mBubbleContainer, false);
-
+ animateDismissBubble(mBubbleContainer, false);
if (wasFlungOut) {
mStackAnimationController.flingStackThenSpringToEdge(
mStackAnimationController.getStackPosition().x, velX, velY);
@@ -481,11 +474,10 @@
mStackAnimationController.animateStackDismissal(
mDismissView.getHeight() /* translationYBy */,
() -> {
- resetDesaturationAndDarken();
+ resetDismissAnimator();
dismissMagnetizedObject();
}
);
-
mDismissView.hide();
}
};
@@ -836,17 +828,7 @@
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
- mDismissView = new DismissView(context);
- addView(mDismissView);
-
- final ContentResolver contentResolver = getContext().getContentResolver();
- final int dismissRadius = Settings.Secure.getInt(
- contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
-
- // Save the MagneticTarget instance for the newly set up view - we'll add this to the
- // MagnetizedObjects.
- mMagneticTarget = new MagnetizedObject.MagneticTarget(
- mDismissView.getCircle(), dismissRadius);
+ setUpDismissView();
setClipChildren(false);
setFocusable(true);
@@ -891,6 +873,7 @@
mRelativeStackPositionBeforeRotation = null;
}
+ setUpDismissView();
if (mIsExpanded) {
// Re-draw bubble row and pointer for new orientation.
beforeExpandedViewAnimation();
@@ -905,30 +888,23 @@
}
removeOnLayoutChangeListener(mOrientationChangedListener);
};
-
- final ColorMatrix animatedMatrix = new ColorMatrix();
- final ColorMatrix darkenMatrix = new ColorMatrix();
-
- mDesaturateAndDarkenAnimator = ValueAnimator.ofFloat(1f, 0f);
- mDesaturateAndDarkenAnimator.addUpdateListener(animation -> {
+ final float maxDismissSize = getResources().getDimensionPixelSize(
+ R.dimen.dismiss_circle_size);
+ final float minDismissSize = getResources().getDimensionPixelSize(
+ R.dimen.dismiss_circle_small);
+ final float sizePercent = minDismissSize / maxDismissSize;
+ mDismissBubbleAnimator = ValueAnimator.ofFloat(1f, 0f);
+ mDismissBubbleAnimator.addUpdateListener(animation -> {
final float animatedValue = (float) animation.getAnimatedValue();
- animatedMatrix.setSaturation(animatedValue);
-
- final float animatedDarkenValue = (1f - animatedValue) * DARKEN_PERCENT;
- darkenMatrix.setScale(
- 1f - animatedDarkenValue /* red */,
- 1f - animatedDarkenValue /* green */,
- 1f - animatedDarkenValue /* blue */,
- 1f /* alpha */);
-
- // Concat the matrices so that the animatedMatrix both desaturates and darkens.
- animatedMatrix.postConcat(darkenMatrix);
-
- // Update the paint and apply it to the bubble container.
- mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
-
- if (mDesaturateAndDarkenTargetView != null) {
- mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
+ if (mDismissView != null) {
+ mDismissView.setPivotX((mDismissView.getRight() - mDismissView.getLeft()) / 2f);
+ mDismissView.setPivotY((mDismissView.getBottom() - mDismissView.getTop()) / 2f);
+ final float scaleValue = Math.max(animatedValue, sizePercent);
+ mDismissView.getCircle().setScaleX(scaleValue);
+ mDismissView.getCircle().setScaleY(scaleValue);
+ }
+ if (mViewBeingDismissed != null) {
+ mViewBeingDismissed.setAlpha(Math.max(animatedValue, 0.7f));
}
});
@@ -1048,6 +1024,23 @@
}
};
+ private void setUpDismissView() {
+ if (mDismissView != null) {
+ removeView(mDismissView);
+ }
+ mDismissView = new DismissView(getContext());
+ addView(mDismissView);
+
+ final ContentResolver contentResolver = getContext().getContentResolver();
+ final int dismissRadius = Settings.Secure.getInt(
+ contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
+
+ // Save the MagneticTarget instance for the newly set up view - we'll add this to the
+ // MagnetizedObjects.
+ mMagneticTarget = new MagnetizedObject.MagneticTarget(
+ mDismissView.getCircle(), dismissRadius);
+ }
+
// TODO: Create ManageMenuView and move setup / animations there
private void setUpManageMenu() {
if (mManageMenu != null) {
@@ -1217,6 +1210,7 @@
public void onThemeChanged() {
setUpFlyout();
setUpManageMenu();
+ setUpDismissView();
updateOverflow();
updateUserEdu();
updateExpandedViewTheme();
@@ -1256,6 +1250,7 @@
updateOverflow();
setUpManageMenu();
setUpFlyout();
+ setUpDismissView();
mBubbleSize = mPositioner.getBubbleSize();
for (Bubble b : mBubbleData.getBubbles()) {
if (b.getIconView() == null) {
@@ -2264,42 +2259,46 @@
}
}
- /** Prepares and starts the desaturate/darken animation on the bubble stack. */
- private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
- mDesaturateAndDarkenTargetView = targetView;
+ /** Prepares and starts the dismiss animation on the bubble stack. */
+ private void animateDismissBubble(View targetView, boolean applyAlpha) {
+ mViewBeingDismissed = targetView;
- if (mDesaturateAndDarkenTargetView == null) {
+ if (mViewBeingDismissed == null) {
return;
}
-
- if (desaturateAndDarken) {
- // Use the animated paint for the bubbles.
- mDesaturateAndDarkenTargetView.setLayerType(
- View.LAYER_TYPE_HARDWARE, mDesaturateAndDarkenPaint);
- mDesaturateAndDarkenAnimator.removeAllListeners();
- mDesaturateAndDarkenAnimator.start();
+ if (applyAlpha) {
+ mDismissBubbleAnimator.removeAllListeners();
+ mDismissBubbleAnimator.start();
} else {
- mDesaturateAndDarkenAnimator.removeAllListeners();
- mDesaturateAndDarkenAnimator.addListener(new AnimatorListenerAdapter() {
+ mDismissBubbleAnimator.removeAllListeners();
+ mDismissBubbleAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- // Stop using the animated paint.
- resetDesaturationAndDarken();
+ resetDismissAnimator();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ resetDismissAnimator();
}
});
- mDesaturateAndDarkenAnimator.reverse();
+ mDismissBubbleAnimator.reverse();
}
}
- private void resetDesaturationAndDarken() {
+ private void resetDismissAnimator() {
+ mDismissBubbleAnimator.removeAllListeners();
+ mDismissBubbleAnimator.cancel();
- mDesaturateAndDarkenAnimator.removeAllListeners();
- mDesaturateAndDarkenAnimator.cancel();
-
- if (mDesaturateAndDarkenTargetView != null) {
- mDesaturateAndDarkenTargetView.setLayerType(View.LAYER_TYPE_NONE, null);
- mDesaturateAndDarkenTargetView = null;
+ if (mViewBeingDismissed != null) {
+ mViewBeingDismissed.setAlpha(1f);
+ mViewBeingDismissed = null;
+ }
+ if (mDismissView != null) {
+ mDismissView.getCircle().setScaleX(1f);
+ mDismissView.getCircle().setScaleY(1f);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index 04b5ad6..0a1cd22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -67,8 +67,6 @@
fun show() {
if (isShowing) return
isShowing = true
- bringToFront()
- setZ(Short.MAX_VALUE - 1f)
setVisibility(View.VISIBLE)
(getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
animator.cancel()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 0049461..e1b198c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -379,6 +379,10 @@
}
}
+ public SurfaceControl getSurfaceControl() {
+ return mLeash;
+ }
+
private void setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params,
ActivityInfo activityInfo) {
mPipBoundsState.setBoundsStateForEntry(componentName,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index c26b686..1da9577 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -26,8 +26,10 @@
import android.graphics.drawable.TransitionDrawable;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -47,7 +49,7 @@
/**
* Handler of all Magnetized Object related code for PiP.
*/
-public class PipDismissTargetHandler {
+public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListener {
/* The multiplier to apply scale the target size by when applying the magnetic field radius */
private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
@@ -92,6 +94,9 @@
private int mDismissAreaHeight;
private float mMagneticFieldRadiusPercent = 1f;
+ private SurfaceControl mTaskLeash;
+ private boolean mHasDismissTargetSurface;
+
private final Context mContext;
private final PipMotionHelper mMotionHelper;
private final PipUiEventLogger mPipUiEventLogger;
@@ -167,6 +172,14 @@
mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
}
+ @Override
+ public boolean onPreDraw() {
+ mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
+ mHasDismissTargetSurface = true;
+ updateDismissTargetLayer();
+ return true;
+ }
+
/**
* Potentially start consuming future motion events if PiP is currently near the magnetized
* object.
@@ -207,12 +220,31 @@
* MAGNETIC_FIELD_RADIUS_MULTIPLIER));
}
+ public void setTaskLeash(SurfaceControl taskLeash) {
+ mTaskLeash = taskLeash;
+ }
+
+ private void updateDismissTargetLayer() {
+ if (!mHasDismissTargetSurface || mTaskLeash == null) {
+ // No dismiss target surface, can just return
+ return;
+ }
+
+ // Put the dismiss target behind the task
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.setRelativeLayer(mTargetViewContainer.getViewRootImpl().getSurfaceControl(),
+ mTaskLeash, -1);
+ t.apply();
+ }
+
/** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
public void createOrUpdateDismissTarget() {
if (!mTargetViewContainer.isAttachedToWindow()) {
mMagneticTargetAnimator.cancel();
mTargetViewContainer.setVisibility(View.INVISIBLE);
+ mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
+ mHasDismissTargetSurface = false;
try {
mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams());
@@ -259,9 +291,9 @@
createOrUpdateDismissTarget();
if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
-
mTargetView.setTranslationY(mTargetViewContainer.getHeight());
mTargetViewContainer.setVisibility(View.VISIBLE);
+ mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this);
// Cancel in case we were in the middle of animating it out.
mMagneticTargetAnimator.cancel();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 15e7f07..604ebc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -40,6 +40,7 @@
import androidx.dynamicanimation.animation.AnimationHandler;
import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
+import com.android.wm.shell.R;
import com.android.wm.shell.animation.FloatProperties;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -71,6 +72,8 @@
/** Friction to use for PIP when it moves via physics fling animations. */
private static final float DEFAULT_FRICTION = 1.9f;
+ /** How much of the dismiss circle size to use when scaling down PIP. **/
+ private static final float DISMISS_CIRCLE_PERCENT = 0.85f;
private final Context mContext;
private final PipTaskOrganizer mPipTaskOrganizer;
@@ -296,9 +299,17 @@
boolean flung, Function0<Unit> after) {
final PointF targetCenter = target.getCenterOnScreen();
- final float desiredWidth = getBounds().width() / 2;
- final float desiredHeight = getBounds().height() / 2;
+ // PIP should fit in the circle
+ final float dismissCircleSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.dismiss_circle_size);
+ final float width = getBounds().width();
+ final float height = getBounds().height();
+ final float ratio = width / height;
+
+ // Width should be a little smaller than the circle size.
+ final float desiredWidth = dismissCircleSize * DISMISS_CIRCLE_PERCENT;
+ final float desiredHeight = desiredWidth / ratio;
final float destinationX = targetCenter.x - (desiredWidth / 2f);
final float destinationY = targetCenter.y - (desiredHeight / 2f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index f0ea465..b1086c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -75,6 +75,7 @@
private final @NonNull PipBoundsState mPipBoundsState;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
+ private final PipTaskOrganizer mPipTaskOrganizer;
private final ShellExecutor mMainExecutor;
private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -173,6 +174,7 @@
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipTaskOrganizer = pipTaskOrganizer;
mMenuController = menuController;
mPipUiEventLogger = pipUiEventLogger;
mFloatingContentCoordinator = floatingContentCoordinator;
@@ -799,6 +801,7 @@
mMovementWithinDismiss = touchState.getDownTouchPosition().y
>= mPipBoundsState.getMovementBounds().bottom;
mMotionHelper.setSpringingToTouch(false);
+ mPipDismissTargetHandler.setTaskLeash(mPipTaskOrganizer.getSurfaceControl());
// If the menu is still visible then just poke the menu
// so that it will timeout after the user stops touching it
@@ -847,6 +850,7 @@
@Override
public boolean onUp(PipTouchState touchState) {
mPipDismissTargetHandler.hideDismissTargetMaybe();
+ mPipDismissTargetHandler.setTaskLeash(null);
if (!touchState.isUserInteracting()) {
return false;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d2947c6..bcb21d1 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -589,7 +589,7 @@
<provider android:name=".HeapDumpProvider"
android:authorities="com.android.shell.heapdump"
android:grantUriPermissions="true"
- android:exported="true" />
+ android:exported="false" />
<activity
android:name=".BugreportWarningActivity"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 34a1452..1a00503 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -630,9 +630,9 @@
58.0001 29.2229,56.9551 26.8945,55.195
</string>
- <!-- The radius of the enrollment progress bar, in pixels -->
+ <!-- The radius of the enrollment progress bar, in dp -->
<integer name="config_udfpsEnrollProgressBar" translatable="false">
- 360
+ 280
</integer>
<!-- package name of a built-in camera app to use to restrict implicit intent resolution
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c95101e..0b8868f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -62,10 +62,9 @@
@Override
protected void setPasswordEntryEnabled(boolean enabled) {
- boolean wasEnabled = mPasswordEntry.isEnabled();
mPasswordEntry.setEnabled(enabled);
mOkButton.setEnabled(enabled);
- if (enabled && !wasEnabled && !mPasswordEntry.hasFocus()) {
+ if (enabled && !mPasswordEntry.hasFocus()) {
mPasswordEntry.requestFocus();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
index 52141c0..da24a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
@@ -31,8 +31,10 @@
/**
* UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
*
- * The call must be made from the UI thread. The callback, if provided, will also be invoked
- * from the UI thread.
+ * This method is a no-op when some type of HBM is already enabled.
+ *
+ * This method must be called from the UI thread. The callback, if provided, will also be
+ * invoked from the UI thread.
*
* @param hbmType The type of HBM that should be enabled. See {@link UdfpsHbmTypes}.
* @param surface The surface for which the HBM is requested, in case the HBM implementation
@@ -45,14 +47,14 @@
/**
* UdfpsView will call this to disable the HBM when the illumination is not longer needed.
*
+ * This method is a no-op when HBM is already disabled. If HBM is enabled, this method will
+ * disable HBM for the {@code hbmType} and {@code surface} that were provided to the
+ * corresponding {@link #enableHbm(int, Surface, Runnable)}.
+ *
* The call must be made from the UI thread. The callback, if provided, will also be invoked
* from the UI thread.
*
- * @param hbmType The type of HBM that should be disabled. See {@link UdfpsHbmTypes}.
- * @param surface The surface for which the HBM is requested, in case the HBM implementation
- * needs to unset special surface flags to disable the HBM. Can be null.
* @param onHbmDisabled A runnable that will be executed once HBM is disabled.
*/
- void disableHbm(@HbmType int hbmType, @Nullable Surface surface,
- @Nullable Runnable onHbmDisabled);
+ void disableHbm(@Nullable Runnable onHbmDisabled);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 86e3ae6..6a6f57a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -124,8 +124,9 @@
@Override
public void stopIllumination() {
if (mHbmProvider != null) {
- final Runnable onHbmDisabled = this::invalidate;
- mHbmProvider.disableHbm(mHbmType, mHolder.getSurface(), onHbmDisabled);
+ final Runnable onHbmDisabled =
+ (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) ? this::invalidate : null;
+ mHbmProvider.disableHbm(onHbmDisabled);
} else {
Log.e(TAG, "stopIllumination | mHbmProvider is null");
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 9dc4ac9..e11e67d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -372,6 +372,9 @@
return;
}
+ final float smallCornerRadius =
+ getResources().getDimension(R.dimen.notification_corner_radius_small)
+ / getResources().getDimension(R.dimen.notification_corner_radius);
final float viewEnd = viewStart + anv.getActualHeight();
final float cornerAnimationDistance = mCornerAnimationDistance
* mAmbientState.getExpansionFraction();
@@ -387,7 +390,7 @@
} else if (viewEnd < cornerAnimationTop) {
// Fast scroll skips frames and leaves corners with unfinished rounding.
// Reset top and bottom corners outside of animation bounds.
- anv.setBottomRoundness(anv.isLastInSection() ? 1f : 0f,
+ anv.setBottomRoundness(anv.isLastInSection() ? 1f : smallCornerRadius,
false /* animate */);
}
@@ -401,7 +404,7 @@
} else if (viewStart < cornerAnimationTop) {
// Fast scroll skips frames and leaves corners with unfinished rounding.
// Reset top and bottom corners outside of animation bounds.
- anv.setTopRoundness(anv.isFirstInSection() ? 1f : 0f,
+ anv.setTopRoundness(anv.isFirstInSection() ? 1f : smallCornerRadius,
false /* animate */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index b60ef1d..5f9c1d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -258,16 +258,14 @@
}
}
- // Save (height of view before shelf, index of first view in shelf) from when shade is fully
+ // Save the index of first view in shelf from when shade is fully
// expanded. Consider updating these states in updateContentView instead so that we don't
// have to recalculate in every frame.
float currentY = -scrollY;
if (!ambientState.isOnKeyguard()) {
currentY += mNotificationScrimPadding;
}
- float previousY = 0;
state.firstViewInShelf = null;
- state.viewHeightBeforeShelf = -1;
for (int i = 0; i < state.visibleChildren.size(); i++) {
final ExpandableView view = state.visibleChildren.get(i);
@@ -285,17 +283,8 @@
&& !(view instanceof FooterView)
&& state.firstViewInShelf == null) {
state.firstViewInShelf = view;
- // There might be a section gap right before the shelf.
- // Limit the height of the view before the shelf so that it does not include
- // a gap and become taller than it normally is.
- state.viewHeightBeforeShelf = Math.min(getMaxAllowedChildHeight(view),
- ambientState.getStackEndHeight()
- - ambientState.getShelf().getIntrinsicHeight()
- - mPaddingBetweenElements
- - previousY);
}
}
- previousY = currentY;
currentY = currentY
+ getMaxAllowedChildHeight(view)
+ mPaddingBetweenElements;
@@ -454,16 +443,7 @@
}
// Clip height of view right before shelf.
- float maxViewHeight = getMaxAllowedChildHeight(view);
- if (ambientState.isExpansionChanging()
- && algorithmState.viewHeightBeforeShelf != -1) {
- final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf(
- algorithmState.firstViewInShelf);
- if (i == indexOfFirstViewInShelf - 1) {
- maxViewHeight = algorithmState.viewHeightBeforeShelf;
- }
- }
- viewState.height = (int) (maxViewHeight * expansionFraction);
+ viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
}
algorithmState.mCurrentYPosition += viewState.height
@@ -737,11 +717,6 @@
public ExpandableView firstViewInShelf;
/**
- * Height of view right before the shelf.
- */
- public float viewHeightBeforeShelf;
-
- /**
* The children from the host view which are not gone.
*/
public final ArrayList<ExpandableView> visibleChildren = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f545710..3a01791 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2203,7 +2203,7 @@
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
&& !mShouldUseSplitNotificationShade;
- setQsExpansionEnabled(mAmbientState.getScrollY() == 0);
+ setQsExpansionEnabled(mAmbientState.getScrollY() == 0 && !mAmbientState.isShadeOpening());
if (!mShouldUseSplitNotificationShade) {
if (mTransitioningToFullShadeProgress > 0.0f) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9abe00f..7eecc45 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -710,6 +710,34 @@
}
@Override
+ public boolean removeClient(IAccessibilityManagerClient callback, int userId) {
+ // TODO(b/190216606): Add tracing for removeClient when implementation is the same in master
+
+ synchronized (mLock) {
+ final int resolvedUserId = mSecurityPolicy
+ .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+
+ AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
+ if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
+ boolean unregistered = mGlobalClients.unregister(callback);
+ if (DEBUG) {
+ Slog.i(LOG_TAG,
+ "Removed global client for pid:" + Binder.getCallingPid() + "state: "
+ + unregistered);
+ }
+ return unregistered;
+ } else {
+ boolean unregistered = userState.mUserClients.unregister(callback);
+ if (DEBUG) {
+ Slog.i(LOG_TAG, "Removed user client for pid:" + Binder.getCallingPid()
+ + " and userId:" + resolvedUserId + "state: " + unregistered);
+ }
+ return unregistered;
+ }
+ }
+ }
+
+ @Override
public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
if (mTraceManager.isA11yTracingEnabled()) {
mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent",
@@ -3271,6 +3299,14 @@
pw.println();
}
mA11yWindowManager.dump(fd, pw, args);
+ pw.println("Global client list info:{");
+ mGlobalClients.dump(pw, " Client list ");
+ pw.println(" Registered clients:{");
+ for (int i = 0; i < mGlobalClients.getRegisteredCallbackCount(); i++) {
+ AccessibilityManagerService.Client client = (AccessibilityManagerService.Client)
+ mGlobalClients.getRegisteredCallbackCookie(i);
+ pw.append(Arrays.toString(client.mPackageNames));
+ }
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index df349c8..0fde0de 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -51,6 +51,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -573,6 +574,15 @@
pw.append(componentName.toShortString());
}
}
+ pw.println("}");
+ pw.println(" Client list info:{");
+ mUserClients.dump(pw, " Client list ");
+ pw.println(" Registered clients:{");
+ for (int i = 0; i < mUserClients.getRegisteredCallbackCount(); i++) {
+ AccessibilityManagerService.Client client = (AccessibilityManagerService.Client)
+ mUserClients.getRegisteredCallbackCookie(i);
+ pw.append(Arrays.toString(client.mPackageNames));
+ }
pw.println("}]");
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6dd4f04..afaddd9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1930,7 +1930,6 @@
decActiveForegroundAppLocked(smap, r);
}
r.isForeground = false;
- resetFgsRestrictionLocked(r);
r.mFgsExitTime = SystemClock.uptimeMillis();
ServiceState stracker = r.getTracker();
if (stracker != null) {
@@ -1945,6 +1944,7 @@
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
r.mFgsExitTime > r.mFgsEnterTime
? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
+ resetFgsRestrictionLocked(r);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
@@ -4192,8 +4192,7 @@
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
- r.mAllowWhileInUsePermissionInFgs = false;
- r.mAllowStartForeground = REASON_DENIED;
+ resetFgsRestrictionLocked(r);
// Clear start entries.
r.clearDeliveredStartsLocked();
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 1b5483a..abb8243 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -24,6 +24,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.Nullable;
import android.app.ApplicationExitInfo;
import android.app.ApplicationExitInfo.Reason;
@@ -287,8 +288,8 @@
if (!mAppExitInfoLoaded.get()) {
return;
}
- mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecord(app))
- .sendToTarget();
+ mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED,
+ obtainRawRecord(app, System.currentTimeMillis())).sendToTarget();
}
void scheduleNoteAppKill(final ProcessRecord app, final @Reason int reason,
@@ -300,7 +301,7 @@
return;
}
- ApplicationExitInfo raw = obtainRawRecord(app);
+ ApplicationExitInfo raw = obtainRawRecord(app, System.currentTimeMillis());
raw.setReason(reason);
raw.setSubReason(subReason);
raw.setDescription(msg);
@@ -542,7 +543,8 @@
return AppExitInfoTracker.FOREACH_ACTION_NONE;
});
- Collections.sort(list, (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+ Collections.sort(list,
+ (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
int size = list.size();
if (maxNum > 0) {
size = Math.min(size, maxNum);
@@ -976,7 +978,7 @@
}
@VisibleForTesting
- ApplicationExitInfo obtainRawRecord(ProcessRecord app) {
+ ApplicationExitInfo obtainRawRecord(ProcessRecord app, @CurrentTimeMillisLong long timestamp) {
ApplicationExitInfo info = mRawRecordsPool.acquire();
if (info == null) {
info = new ApplicationExitInfo();
@@ -998,7 +1000,7 @@
info.setImportance(procStateToImportance(app.mState.getSetProcState()));
info.setPss(app.mProfile.getLastPss());
info.setRss(app.mProfile.getLastRss());
- info.setTimestamp(System.currentTimeMillis());
+ info.setTimestamp(timestamp);
}
return info;
@@ -1298,7 +1300,7 @@
results.add(mInfos.valueAt(i));
}
Collections.sort(results,
- (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+ (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
} else {
if (maxNum == 1) {
// Most of the caller might be only interested with the most recent one
@@ -1318,7 +1320,7 @@
list.add(mInfos.valueAt(i));
}
Collections.sort(list,
- (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+ (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
for (int i = 0; i < maxNum; i++) {
results.add(list.get(i));
}
@@ -1412,7 +1414,7 @@
for (int i = mInfos.size() - 1; i >= 0; i--) {
list.add(mInfos.valueAt(i));
}
- Collections.sort(list, (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+ Collections.sort(list, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
int size = list.size();
for (int i = 0; i < size; i++) {
list.get(i).dump(pw, prefix + " ", "#" + i, sdf);
@@ -1629,7 +1631,8 @@
}
}
- private static boolean isFresh(long timestamp) {
+ @VisibleForTesting
+ boolean isFresh(long timestamp) {
// A process could be dying but being stuck in some state, i.e.,
// being TRACED by tombstoned, thus the zygote receives SIGCHILD
// way after we already knew the kill (maybe because we did the kill :P),
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 9a96e53..ab1da80 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -501,37 +501,38 @@
mService.grantImplicitAccess(userId, null, callingUid,
UserHandle.getAppId(cpi.applicationInfo.uid));
- }
- if (caller != null) {
- // The client will be waiting, and we'll notify it when the provider is ready.
- synchronized (cpr) {
- if (cpr.provider == null) {
- if (cpr.launchingApp == null) {
- Slog.w(TAG, "Unable to launch app "
- + cpi.applicationInfo.packageName + "/"
- + cpi.applicationInfo.uid + " for provider "
- + name + ": launching app became null");
- EventLogTags.writeAmProviderLostProcess(
- UserHandle.getUserId(cpi.applicationInfo.uid),
- cpi.applicationInfo.packageName,
- cpi.applicationInfo.uid, name);
- return null;
- }
+ if (caller != null) {
+ // The client will be waiting, and we'll notify it when the provider is ready.
+ synchronized (cpr) {
+ if (cpr.provider == null) {
+ if (cpr.launchingApp == null) {
+ Slog.w(TAG, "Unable to launch app "
+ + cpi.applicationInfo.packageName + "/"
+ + cpi.applicationInfo.uid + " for provider "
+ + name + ": launching app became null");
+ EventLogTags.writeAmProviderLostProcess(
+ UserHandle.getUserId(cpi.applicationInfo.uid),
+ cpi.applicationInfo.packageName,
+ cpi.applicationInfo.uid, name);
+ return null;
+ }
- if (conn != null) {
- conn.waiting = true;
+ if (conn != null) {
+ conn.waiting = true;
+ }
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG);
+ msg.obj = cpr;
+ mService.mHandler.sendMessageDelayed(msg,
+ ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
}
- Message msg = mService.mHandler.obtainMessage(
- ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG);
- msg.obj = cpr;
- mService.mHandler.sendMessageDelayed(msg,
- ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
}
+ // Return a holder instance even if we are waiting for the publishing of the
+ // provider, client will check for the holder.provider to see if it needs to wait
+ // for it.
+ return cpr.newHolder(conn, false);
}
- // Return a holder instance even if we are waiting for the publishing of the provider,
- // client will check for the holder.provider to see if it needs to wait for it.
- return cpr.newHolder(conn, false);
}
// Because of the provider's external client (i.e., SHELL), we'll have to wait right here.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 00a68a0..be2a63d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2684,9 +2684,9 @@
} else {
result.addAll(approvedInfos);
- // If the other profile has an app that's of equal or higher approval, add it
+ // If the other profile has an app that's higher approval, add it
if (xpDomainInfo != null
- && xpDomainInfo.highestApprovalLevel >= highestApproval) {
+ && xpDomainInfo.highestApprovalLevel > highestApproval) {
result.add(xpDomainInfo.resolveInfo);
}
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index f39d618..a6c93de 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -43,8 +43,6 @@
import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
-import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.TriFunction;
@@ -321,6 +319,17 @@
if (appIdTags == null) {
appIdTags = new ArrayMap<>();
}
+
+ // Remove any invalid tags
+ boolean nullRemoved = packageTags.remove(null);
+ boolean nullStrRemoved = packageTags.remove("null");
+ boolean emptyRemoved = packageTags.remove("");
+ if (nullRemoved || nullStrRemoved || emptyRemoved) {
+ Log.e(LOG_TAG, "Attempted to add invalid source attribution tag, removed "
+ + "null: " + nullRemoved + " removed \"null\": " + nullStrRemoved
+ + " removed empty string: " + emptyRemoved);
+ }
+
appIdTags.put(packageName, packageTags);
datastore.put(appId, appIdTags);
} else if (appIdTags != null) {
diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
index 42a2f81..bf54bd5 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
@@ -16,9 +16,12 @@
package com.android.server.utils;
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
import android.annotation.Nullable;
import android.annotation.Size;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -39,13 +42,14 @@
public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappable {
/**
- * The matrix is implemented through four arrays. The matrix of booleans is stored in
- * a one-dimensional {@code mValues} array. {@code mValues} is always of size
- * {@code mOrder * mOrder}. Elements of {@code mValues} are addressed with
- * arithmetic: the offset of the element {@code {row, col}} is at
- * {@code row * mOrder + col}. The term "storage index" applies to {@code mValues}.
- * A storage index designates a row (column) in the underlying storage. This is not
- * the same as the row seen by client code.
+ * The matrix is implemented through four arrays. First, the matrix of booleans is
+ * stored in a two-dimensional {@code mValues} array of bit-packed booleans.
+ * {@code mValues} is always of size {@code mOrder * mOrder / 8}. The factor of 8 is
+ * present because there are 8 bits in a byte. Elements of {@code mValues} are
+ * addressed with arithmetic: the element {@code {row, col}} is bit {@code col % 8} in
+ * byte * {@code (row * mOrder + col) / 8}. The term "storage index" applies to
+ * {@code mValues}. A storage index designates a row (column) in the underlying
+ * storage. This is not the same as the row seen by client code.
*
* Client code addresses the matrix through indices. These are integers that need not
* be contiguous. Client indices are mapped to storage indices through two linear
@@ -61,16 +65,32 @@
*
* Some notes:
* <ul>
- * <li> The matrix never shrinks.
+ * <li> The matrix does not automatically shrink but there is a compress() method that
+ * will recover unused space.
* <li> Equality is a very, very expesive operation.
* </ul>
*/
/**
* mOrder is always a multiple of this value. A minimal matrix therefore holds 2^12
- * values and requires 1024 bytes.
+ * values and requires 1024 bytes. The value is visible for testing.
*/
- private static final int STEP = 64;
+ @VisibleForTesting(visibility = PRIVATE)
+ static final int STEP = 64;
+
+ /**
+ * There are 8 bits in a byte. The constant is defined here only to make it easy to
+ * find in the code.
+ */
+ private static final int BYTE = 8;
+
+ /**
+ * Constants that index into the string array returned by matrixToString. The primary
+ * consumer is test code.
+ */
+ static final int STRING_KEY_INDEX = 0;
+ static final int STRING_MAP_INDEX = 1;
+ static final int STRING_INUSE_INDEX = 2;
/**
* The order of the matrix storage, including any padding. The matrix is always
@@ -103,7 +123,7 @@
/**
* The boolean array. This array is always {@code mOrder x mOrder} in size.
*/
- private boolean[] mValues;
+ private byte[] mValues;
/**
* A convenience function called when the elements are added to or removed from the storage.
@@ -140,7 +160,7 @@
mInUse = new boolean[mOrder];
mKeys = ArrayUtils.newUnpaddedIntArray(mOrder);
mMap = ArrayUtils.newUnpaddedIntArray(mOrder);
- mValues = new boolean[mOrder * mOrder];
+ mValues = new byte[mOrder * mOrder / 8];
mSize = 0;
}
@@ -207,7 +227,7 @@
}
if (r >= 0 && c >= 0) {
setValueAt(r, c, value);
- onChanged();
+ // setValueAt() will call onChanged().
} else {
throw new RuntimeException("matrix overflow");
}
@@ -232,8 +252,12 @@
public void removeAt(int index) {
validateIndex(index);
mInUse[mMap[index]] = false;
+ // Remove the specified index and ensure that unused words in mKeys and mMap are
+ // always zero, to simplify the equality function.
System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+ mKeys[mSize - 1] = 0;
System.arraycopy(mMap, index + 1, mMap, index, mSize - (index + 1));
+ mMap[mSize - 1] = 0;
mSize--;
onChanged();
}
@@ -272,6 +296,17 @@
}
/**
+ * An internal method to fetch the boolean value given the mValues row and column
+ * indices. These are not the indices used by the *At() methods.
+ */
+ private boolean valueAtInternal(int row, int col) {
+ int element = row * mOrder + col;
+ int offset = element / BYTE;
+ int mask = 1 << (element % BYTE);
+ return (mValues[offset] & mask) != 0;
+ }
+
+ /**
* Given a row and column, each in the range <code>0...size()-1</code>, returns the
* value from the <code>index</code>th key-value mapping that this WatchedSparseBooleanMatrix
* stores.
@@ -280,8 +315,22 @@
validateIndex(rowIndex, colIndex);
int r = mMap[rowIndex];
int c = mMap[colIndex];
- int element = r * mOrder + c;
- return mValues[element];
+ return valueAtInternal(r, c);
+ }
+
+ /**
+ * An internal method to set the boolean value given the mValues row and column
+ * indices. These are not the indices used by the *At() methods.
+ */
+ private void setValueAtInternal(int row, int col, boolean value) {
+ int element = row * mOrder + col;
+ int offset = element / BYTE;
+ byte mask = (byte) (1 << (element % BYTE));
+ if (value) {
+ mValues[offset] |= mask;
+ } else {
+ mValues[offset] &= ~mask;
+ }
}
/**
@@ -291,8 +340,7 @@
validateIndex(rowIndex, colIndex);
int r = mMap[rowIndex];
int c = mMap[colIndex];
- int element = r * mOrder + c;
- mValues[element] = value;
+ setValueAtInternal(r, c, value);
onChanged();
}
@@ -327,12 +375,17 @@
mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
mMap = GrowingArrayUtils.insert(mMap, mSize, i, newIndex);
mSize++;
+
// Initialize the row and column corresponding to the new index.
+ int valueRow = mOrder / BYTE;
+ int offset = newIndex / BYTE;
+ byte mask = (byte) (~(1 << (newIndex % BYTE)));
+ Arrays.fill(mValues, newIndex * valueRow, (newIndex + 1) * valueRow, (byte) 0);
for (int n = 0; n < mSize; n++) {
- mValues[n * mOrder + newIndex] = false;
- mValues[newIndex * mOrder + n] = false;
+ mValues[n * valueRow + offset] &= mask;
}
- onChanged();
+ // Do not report onChanged() from this private method. onChanged() is the
+ // responsibility of public methods that call this one.
}
return i;
}
@@ -356,6 +409,33 @@
}
/**
+ * Expand the 2D array. This also extends the free list.
+ */
+ private void growMatrix() {
+ resizeValues(mOrder + STEP);
+ }
+
+ /**
+ * Resize the values array to the new dimension.
+ */
+ private void resizeValues(int newOrder) {
+
+ boolean[] newInuse = Arrays.copyOf(mInUse, newOrder);
+ int minOrder = Math.min(mOrder, newOrder);
+
+ byte[] newValues = new byte[newOrder * newOrder / BYTE];
+ for (int i = 0; i < minOrder; i++) {
+ int row = mOrder * i / BYTE;
+ int newRow = newOrder * i / BYTE;
+ System.arraycopy(mValues, row, newValues, newRow, minOrder / BYTE);
+ }
+
+ mInUse = newInuse;
+ mValues = newValues;
+ mOrder = newOrder;
+ }
+
+ /**
* Find an unused storage index, mark it in-use, and return it.
*/
private int nextFree() {
@@ -369,27 +449,82 @@
}
/**
- * Expand the 2D array. This also extends the free list.
+ * Return the index of the key that uses the highest row index in use. This returns
+ * -1 if the matrix is empty. Note that the return is an index suitable for the *At()
+ * methods. It is not the index in the mInUse array.
*/
- private void growMatrix() {
- int newOrder = mOrder + STEP;
-
- boolean[] newInuse = Arrays.copyOf(mInUse, newOrder);
-
- boolean[] newValues = new boolean[newOrder * newOrder];
- for (int i = 0; i < mOrder; i++) {
- int row = mOrder * i;
- int newRow = newOrder * i;
- for (int j = 0; j < mOrder; j++) {
- int index = row + j;
- int newIndex = newRow + j;
- newValues[newIndex] = mValues[index];
+ private int lastInuse() {
+ for (int i = mOrder - 1; i >= 0; i--) {
+ if (mInUse[i]) {
+ for (int j = 0; j < mSize; j++) {
+ if (mMap[j] == i) {
+ return j;
+ }
+ }
+ throw new IndexOutOfBoundsException();
}
}
+ return -1;
+ }
- mInUse = newInuse;
- mValues = newValues;
- mOrder = newOrder;
+ /**
+ * Compress the matrix by packing keys into consecutive indices. If the compression
+ * is sufficient, the mValues array can be shrunk.
+ */
+ private void pack() {
+ if (mSize == 0 || mSize == mOrder) {
+ return;
+ }
+ // dst and src are identify raw (row, col) in mValues. srcIndex is the index (as
+ // in the result of keyAt()) of the key being relocated.
+ for (int dst = nextFree(); dst < mSize; dst = nextFree()) {
+ int srcIndex = lastInuse();
+ int src = mMap[srcIndex];
+ mInUse[src] = false;
+ mMap[srcIndex] = dst;
+ System.arraycopy(mValues, src * mOrder / BYTE,
+ mValues, dst * mOrder / BYTE,
+ mOrder / BYTE);
+ int srcOffset = (src / BYTE);
+ byte srcMask = (byte) (1 << (src % BYTE));
+ int dstOffset = (dst / BYTE);
+ byte dstMask = (byte) (1 << (dst % BYTE));
+ for (int i = 0; i < mOrder; i++) {
+ if ((mValues[srcOffset] & srcMask) == 0) {
+ mValues[dstOffset] &= ~dstMask;
+ } else {
+ mValues[dstOffset] |= dstMask;
+ }
+ srcOffset += mOrder / BYTE;
+ dstOffset += mOrder / BYTE;
+ }
+ }
+ }
+
+ /**
+ * Shrink the matrix, if possible.
+ */
+ public void compact() {
+ pack();
+ int unused = (mOrder - mSize) / STEP;
+ if (unused > 0) {
+ resizeValues(mOrder - (unused * STEP));
+ }
+ }
+
+ /**
+ * Return a copy of the keys that are in use by the matrix.
+ */
+ public int[] keys() {
+ return Arrays.copyOf(mKeys, mSize);
+ }
+
+ /**
+ * Return the size of the 2D matrix. This is always greater than or equal to size().
+ * This does not reflect the sizes of the meta-information arrays (such as mKeys).
+ */
+ public int capacity() {
+ return mOrder;
}
/**
@@ -398,15 +533,12 @@
@Override
public int hashCode() {
int hashCode = mSize;
+ hashCode = 31 * hashCode + Arrays.hashCode(mKeys);
+ hashCode = 31 * hashCode + Arrays.hashCode(mMap);
for (int i = 0; i < mSize; i++) {
- hashCode = 31 * hashCode + mKeys[i];
- hashCode = 31 * hashCode + mMap[i];
- }
- for (int i = 0; i < mSize; i++) {
- int row = mMap[i] * mOrder;
+ int row = mMap[i];
for (int j = 0; j < mSize; j++) {
- int element = mMap[j] + row;
- hashCode = 31 * hashCode + (mValues[element] ? 1 : 0);
+ hashCode = 31 * hashCode + (valueAtInternal(row, mMap[j]) ? 1 : 0);
}
}
return hashCode;
@@ -429,20 +561,16 @@
if (mSize != other.mSize) {
return false;
}
-
- for (int i = 0; i < mSize; i++) {
- if (mKeys[i] != other.mKeys[i]) {
- return false;
- }
- if (mMap[i] != other.mMap[i]) {
- return false;
- }
+ if (!Arrays.equals(mKeys, other.mKeys)) {
+ // mKeys is zero padded at the end and is sorted, so the arrays can always be
+ // directly compared.
+ return false;
}
for (int i = 0; i < mSize; i++) {
- int row = mMap[i] * mOrder;
+ int row = mMap[i];
for (int j = 0; j < mSize; j++) {
- int element = mMap[j] + row;
- if (mValues[element] != other.mValues[element]) {
+ int col = mMap[j];
+ if (valueAtInternal(row, col) != other.valueAtInternal(row, col)) {
return false;
}
}
@@ -451,9 +579,12 @@
}
/**
- * Return the matrix meta information. This is always three strings long.
+ * Return the matrix meta information. This is always three strings long. The
+ * strings are indexed by the constants STRING_KEY_INDEX, STRING_MAP_INDEX, and
+ * STRING_INUSE_INDEX.
*/
- private @Size(3) String[] matrixToStringMeta() {
+ @VisibleForTesting(visibility = PRIVATE)
+ @Size(3) String[] matrixToStringMeta() {
String[] result = new String[3];
StringBuilder k = new StringBuilder();
@@ -463,7 +594,7 @@
k.append(" ");
}
}
- result[0] = k.substring(0);
+ result[STRING_KEY_INDEX] = k.substring(0);
StringBuilder m = new StringBuilder();
for (int i = 0; i < mSize; i++) {
@@ -472,42 +603,47 @@
m.append(" ");
}
}
- result[1] = m.substring(0);
+ result[STRING_MAP_INDEX] = m.substring(0);
StringBuilder u = new StringBuilder();
for (int i = 0; i < mOrder; i++) {
u.append(mInUse[i] ? "1" : "0");
}
- result[2] = u.substring(0);
+ result[STRING_INUSE_INDEX] = u.substring(0);
return result;
}
/**
* Return the matrix as an array of strings. There is one string per row. Each
- * string has a '1' or a '0' in the proper column.
+ * string has a '1' or a '0' in the proper column. This is the raw data indexed by
+ * row/column disregarding the key map.
*/
- private String[] matrixToStringRaw() {
+ @VisibleForTesting(visibility = PRIVATE)
+ String[] matrixToStringRaw() {
String[] result = new String[mOrder];
for (int i = 0; i < mOrder; i++) {
- int row = i * mOrder;
StringBuilder line = new StringBuilder(mOrder);
for (int j = 0; j < mOrder; j++) {
- int element = row + j;
- line.append(mValues[element] ? "1" : "0");
+ line.append(valueAtInternal(i, j) ? "1" : "0");
}
result[i] = line.substring(0);
}
return result;
}
- private String[] matrixToStringCooked() {
+ /**
+ * Return the matrix as an array of strings. There is one string per row. Each
+ * string has a '1' or a '0' in the proper column. This is the cooked data indexed by
+ * keys, in key order.
+ */
+ @VisibleForTesting(visibility = PRIVATE)
+ String[] matrixToStringCooked() {
String[] result = new String[mSize];
for (int i = 0; i < mSize; i++) {
- int row = mMap[i] * mOrder;
+ int row = mMap[i];
StringBuilder line = new StringBuilder(mSize);
for (int j = 0; j < mSize; j++) {
- int element = row + mMap[j];
- line.append(mValues[element] ? "1" : "0");
+ line.append(valueAtInternal(row, mMap[j]) ? "1" : "0");
}
result[i] = line.substring(0);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c888e54..53f1035 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -628,7 +628,7 @@
}
// scale if the crop height winds up not matching the recommended metrics
- needScale = wpData.mHeight != cropHint.height()
+ needScale = cropHint.height() > wpData.mHeight
|| cropHint.height() > GLHelper.getMaxTextureSize()
|| cropHint.width() > GLHelper.getMaxTextureSize();
@@ -752,7 +752,7 @@
f = new FileOutputStream(wallpaper.cropFile);
bos = new BufferedOutputStream(f, 32*1024);
- finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
+ finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
bos.flush(); // don't rely on the implicit flush-at-close when noting success
success = true;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index d220444..803a0c1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -36,10 +36,12 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import android.annotation.CurrentTimeMillisLong;
import android.app.ApplicationExitInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -163,14 +165,15 @@
}
}
- private void updateExitInfo(ProcessRecord app) {
- ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app);
+ private void updateExitInfo(ProcessRecord app, @CurrentTimeMillisLong long timestamp) {
+ ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app, timestamp);
mAppExitInfoTracker.handleNoteProcessDiedLocked(raw);
mAppExitInfoTracker.recycleRawRecord(raw);
}
- private void noteAppKill(ProcessRecord app, int reason, int subReason, String msg) {
- ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app);
+ private void noteAppKill(ProcessRecord app, int reason, int subReason, String msg,
+ @CurrentTimeMillisLong long timestamp) {
+ ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app, timestamp);
raw.setReason(reason);
raw.setSubReason(subReason);
raw.setDescription(msg);
@@ -190,6 +193,7 @@
// Test application calls System.exit()
doNothing().when(mAppExitInfoTracker).schedulePersistProcessExitInfo(anyBoolean());
+ doReturn(true).when(mAppExitInfoTracker).isFresh(anyLong());
final int app1Uid = 10123;
final int app1Pid1 = 12345;
@@ -216,7 +220,7 @@
final byte[] app1Cookie2 = {(byte) 0x08, (byte) 0x07, (byte) 0x06, (byte) 0x05,
(byte) 0x04, (byte) 0x03, (byte) 0x02, (byte) 0x01};
- final long now1 = System.currentTimeMillis();
+ final long now1 = 1;
ProcessRecord app = makeProcessRecord(
app1Pid1, // pid
app1Uid, // uid
@@ -240,7 +244,7 @@
doReturn(null)
.when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
.remove(anyInt(), anyInt());
- updateExitInfo(app);
+ updateExitInfo(app, now1);
ArrayList<ApplicationExitInfo> list = new ArrayList<ApplicationExitInfo>();
mAppExitInfoTracker.getExitInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
@@ -290,11 +294,11 @@
.when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
.remove(anyInt(), anyInt());
noteAppKill(app, ApplicationExitInfo.REASON_USER_REQUESTED,
- ApplicationExitInfo.SUBREASON_UNKNOWN, null);
+ ApplicationExitInfo.SUBREASON_UNKNOWN, null, now1s);
// Case 2: create another app1 process record with a different pid
sleep(1);
- final long now2 = System.currentTimeMillis();
+ final long now2 = 2;
app = makeProcessRecord(
app1Pid2, // pid
app1Uid, // uid
@@ -316,16 +320,15 @@
doReturn(new Pair<Long, Object>(now2, Integer.valueOf(makeExitStatus(exitCode))))
.when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
.remove(anyInt(), anyInt());
- updateExitInfo(app);
+ updateExitInfo(app, now2);
list.clear();
// Get all the records for app1Uid
mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
assertEquals(3, list.size());
- info = list.get(0);
+ info = list.get(1);
- // Verify the most recent one
verifyApplicationExitInfo(
info, // info
now2, // timestamp
@@ -346,7 +349,7 @@
assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie2,
app1Cookie2.length));
- info = list.get(1);
+ info = list.get(0);
verifyApplicationExitInfo(
info, // info
now1s, // timestamp
@@ -386,7 +389,7 @@
doReturn(new Pair<Long, Object>(now3, Integer.valueOf(makeSignalStatus(sigNum))))
.when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
.remove(anyInt(), anyInt());
- updateExitInfo(app);
+ updateExitInfo(app, now3);
list.clear();
mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
@@ -463,7 +466,7 @@
app2Rss1, // rss
app2ProcessName, // processName
app2PackageName); // packageName
- updateExitInfo(app);
+ updateExitInfo(app, now4);
list.clear();
mAppExitInfoTracker.getExitInfo(app2PackageName, app2UidUser2, app2PidUser2, 0, list);
assertEquals(1, list.size());
@@ -523,9 +526,9 @@
app3ProcessName, // processName
app3PackageName); // packageName
noteAppKill(app, ApplicationExitInfo.REASON_CRASH_NATIVE,
- ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description);
+ ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description, now5);
- updateExitInfo(app);
+ updateExitInfo(app, now5);
list.clear();
mAppExitInfoTracker.getExitInfo(app3PackageName, app3UidUser2, app3PidUser2, 0, list);
assertEquals(1, list.size());
@@ -648,11 +651,11 @@
app3PackageName); // packageName
mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app3IsolatedUid, app3Uid);
noteAppKill(app, ApplicationExitInfo.REASON_CRASH,
- ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description2);
+ ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description2, now6);
assertEquals(app3Uid, mAppExitInfoTracker.mIsolatedUidRecords
.getUidByIsolatedUid(app3IsolatedUid).longValue());
- updateExitInfo(app);
+ updateExitInfo(app, now6);
assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(app3IsolatedUid));
list.clear();
@@ -736,9 +739,9 @@
mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2);
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_UNKNOWN, app1Description);
+ ApplicationExitInfo.SUBREASON_UNKNOWN, app1Description, now8);
- updateExitInfo(app);
+ updateExitInfo(app, now8);
list.clear();
mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 1, list);
assertEquals(1, list.size());
@@ -802,8 +805,8 @@
traceFile, traceStart, traceEnd);
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2);
- updateExitInfo(app);
+ ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2, now9);
+ updateExitInfo(app, now9);
list.clear();
mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1Pid2User2, 1, list);
assertEquals(1, list.size());
@@ -859,7 +862,7 @@
mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
assertEquals(3, list.size());
- info = list.get(0);
+ info = list.get(1);
exitCode = 6;
verifyApplicationExitInfo(
@@ -879,7 +882,7 @@
IMPORTANCE_SERVICE, // importance
null); // description
- info = list.get(1);
+ info = list.get(0);
verifyApplicationExitInfo(
info, // info
now1s, // timestamp
diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
index 5db9492..f361f4a 100644
--- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
@@ -22,7 +22,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -35,7 +34,6 @@
import org.junit.Test;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Random;
/**
@@ -869,12 +867,34 @@
mSeed = seed;
mRandom = new Random(mSeed);
}
- public int index() {
+ public int next() {
return mRandom.nextInt(50000);
}
public void reset() {
mRandom.setSeed(mSeed);
}
+ // This is an inefficient way to know if a value appears in an array.
+ private boolean contains(int[] s, int length, int k) {
+ for (int i = 0; i < length; i++) {
+ if (s[i] == k) {
+ return true;
+ }
+ }
+ return false;
+ }
+ public int[] indexes(int size) {
+ reset();
+ int[] r = new int[size];
+ for (int i = 0; i < size; i++) {
+ int key = next();
+ // Ensure the list of indices are unique.
+ while (contains(r, i, key)) {
+ key = next();
+ }
+ r[i] = key;
+ }
+ return r;
+ }
}
// Return a value based on the row and column. The algorithm tries to avoid simple
@@ -883,28 +903,8 @@
return (((row * 4 + col) % 3)& 1) == 1;
}
- // This is an inefficient way to know if a value appears in an array.
- private final boolean contains(int[] s, int length, int k) {
- for (int i = 0; i < length; i++) {
- if (s[i] == k) {
- return true;
- }
- }
- return false;
- }
-
- private void matrixTest(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
- indexer.reset();
- int[] indexes = new int[size];
- for (int i = 0; i < size; i++) {
- int key = indexer.index();
- // Ensure the list of indices are unique.
- while (contains(indexes, i, key)) {
- key = indexer.index();
- }
- indexes[i] = key;
- }
- // Set values in the matrix.
+ // Fill a matrix
+ private void fill(WatchedSparseBooleanMatrix matrix, int size, int[] indexes) {
for (int i = 0; i < size; i++) {
int row = indexes[i];
for (int j = 0; j < size; j++) {
@@ -913,21 +913,39 @@
matrix.put(row, col, want);
}
}
+ }
- assertEquals(matrix.size(), size);
-
- // Read back and verify
+ // Verify the content of a matrix. This asserts on mismatch. Selected indices may
+ // have been deleted.
+ private void verify(WatchedSparseBooleanMatrix matrix, int[] indexes, boolean[] absent) {
for (int i = 0; i < matrix.size(); i++) {
int row = indexes[i];
for (int j = 0; j < matrix.size(); j++) {
int col = indexes[j];
- boolean want = cellValue(i, j);
- boolean actual = matrix.get(row, col);
- String msg = String.format("matrix(%d:%d, %d:%d) == %s, expected %s",
- i, row, j, col, actual, want);
- assertEquals(msg, actual, want);
+ if (absent != null && (absent[i] || absent[j])) {
+ boolean want = false;
+ String msg = String.format("matrix(%d:%d, %d:%d) (deleted)", i, row, j, col);
+ assertEquals(msg, matrix.get(row, col), false);
+ assertEquals(msg, matrix.get(row, col, false), false);
+ assertEquals(msg, matrix.get(row, col, true), true);
+ } else {
+ boolean want = cellValue(i, j);
+ String msg = String.format("matrix(%d:%d, %d:%d)", i, row, j, col);
+ assertEquals(msg, matrix.get(row, col), want);
+ assertEquals(msg, matrix.get(row, col, false), want);
+ assertEquals(msg, matrix.get(row, col, true), want);
+ }
}
}
+ }
+
+ private void matrixGrow(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
+ int[] indexes = indexer.indexes(size);
+
+ // Set values in the matrix, then read back and verify.
+ fill(matrix, size, indexes);
+ assertEquals(matrix.size(), size);
+ verify(matrix, indexes, null);
// Test the keyAt/indexOfKey methods
for (int i = 0; i < matrix.size(); i++) {
@@ -936,17 +954,101 @@
}
}
+ private void matrixDelete(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
+ int[] indexes = indexer.indexes(size);
+ fill(matrix, size, indexes);
+
+ // Delete a bunch of rows. Verify that reading back results in false and that
+ // contains() is false. Recreate the rows and verify that all cells (other than
+ // the one just created) are false.
+ boolean[] absent = new boolean[size];
+ for (int i = 0; i < size; i += 13) {
+ matrix.deleteKey(indexes[i]);
+ absent[i] = true;
+ }
+ verify(matrix, indexes, absent);
+ }
+
+ private void matrixShrink(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
+ int[] indexes = indexer.indexes(size);
+ fill(matrix, size, indexes);
+
+ int initialCapacity = matrix.capacity();
+
+ // Delete every other row, remembering which rows were deleted. The goal is to
+ // make room for compaction.
+ boolean[] absent = new boolean[size];
+ for (int i = 0; i < size; i += 2) {
+ matrix.deleteKey(indexes[i]);
+ absent[i] = true;
+ }
+
+ matrix.compact();
+ int finalCapacity = matrix.capacity();
+ assertTrue("Matrix shrink", initialCapacity > finalCapacity);
+ assertTrue("Matrix shrink", finalCapacity - matrix.size() < matrix.STEP);
+ }
+
@Test
public void testWatchedSparseBooleanMatrix() {
final String name = "WatchedSparseBooleanMatrix";
- // The first part of this method tests the core matrix functionality. The second
- // part tests the watchable behavior. The third part tests the snappable
- // behavior.
+ // Test the core matrix functionality. The three tess are meant to test various
+ // combinations of auto-grow.
IndexGenerator indexer = new IndexGenerator(3);
- matrixTest(new WatchedSparseBooleanMatrix(), 10, indexer);
- matrixTest(new WatchedSparseBooleanMatrix(1000), 500, indexer);
- matrixTest(new WatchedSparseBooleanMatrix(1000), 2000, indexer);
+ matrixGrow(new WatchedSparseBooleanMatrix(), 10, indexer);
+ matrixGrow(new WatchedSparseBooleanMatrix(1000), 500, indexer);
+ matrixGrow(new WatchedSparseBooleanMatrix(1000), 2000, indexer);
+ matrixDelete(new WatchedSparseBooleanMatrix(), 500, indexer);
+ matrixShrink(new WatchedSparseBooleanMatrix(), 500, indexer);
+
+ // Test Watchable behavior.
+ WatchedSparseBooleanMatrix matrix = new WatchedSparseBooleanMatrix();
+ WatchableTester tester = new WatchableTester(matrix, name);
+ tester.verify(0, "Initial array - no registration");
+ matrix.put(INDEX_A, INDEX_A, true);
+ tester.verify(0, "Updates with no registration");
+ tester.register();
+ tester.verify(0, "Updates with no registration");
+ matrix.put(INDEX_A, INDEX_B, true);
+ tester.verify(1, "Single cell assignment");
+ matrix.put(INDEX_A, INDEX_B, true);
+ tester.verify(2, "Single cell assignment - same value");
+ matrix.put(INDEX_C, INDEX_B, true);
+ tester.verify(3, "Single cell assignment");
+ matrix.deleteKey(INDEX_B);
+ tester.verify(4, "Delete key");
+ assertEquals(matrix.get(INDEX_B, INDEX_C), false);
+ assertEquals(matrix.get(INDEX_B, INDEX_C, false), false);
+ assertEquals(matrix.get(INDEX_B, INDEX_C, true), true);
+
+ matrix.clear();
+ tester.verify(5, "Clear");
+ assertEquals(matrix.size(), 0);
+ fill(matrix, 10, indexer.indexes(10));
+ int[] keys = matrix.keys();
+ assertEquals(keys.length, matrix.size());
+ for (int i = 0; i < matrix.size(); i++) {
+ assertEquals(matrix.keyAt(i), keys[i]);
+ }
+
+ WatchedSparseBooleanMatrix a = new WatchedSparseBooleanMatrix();
+ matrixGrow(a, 10, indexer);
+ assertEquals(a.size(), 10);
+ WatchedSparseBooleanMatrix b = new WatchedSparseBooleanMatrix();
+ matrixGrow(b, 10, indexer);
+ assertEquals(b.size(), 10);
+ assertEquals(a.equals(b), true);
+ int rowIndex = b.keyAt(3);
+ int colIndex = b.keyAt(4);
+ b.put(rowIndex, colIndex, !b.get(rowIndex, colIndex));
+ assertEquals(a.equals(b), false);
+
+ // Test Snappable behavior.
+ WatchedSparseBooleanMatrix s = a.snapshot();
+ assertEquals(a.equals(s), true);
+ a.put(rowIndex, colIndex, !a.get(rowIndex, colIndex));
+ assertEquals(a.equals(s), false);
}
@Test
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 9574d27..145d7f8 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -88,10 +88,12 @@
COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
VERSION_MAJOR_ATTR = 0x01010577,
PACKAGE_TYPE_ATTR = 0x01010587,
+ USES_PERMISSION_FLAGS_ATTR = 0x01010644,
};
const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
constexpr int kCurrentDevelopmentVersion = 10000;
+constexpr int kNeverForLocation = 0x00010000;
/** Retrieves the attribute of the element with the specified attribute resource id. */
static xml::Attribute* FindAttribute(xml::Element *el, uint32_t resd_id) {
@@ -1070,6 +1072,7 @@
std::vector<std::string> requiredNotFeatures;
int32_t required = true;
int32_t maxSdkVersion = -1;
+ int32_t usesPermissionFlags = 0;
void Extract(xml::Element* element) override {
name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
@@ -1086,6 +1089,8 @@
required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
maxSdkVersion = GetAttributeIntegerDefault(
FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
+ usesPermissionFlags = GetAttributeIntegerDefault(
+ FindAttribute(element, USES_PERMISSION_FLAGS_ATTR), 0);
if (!name.empty()) {
CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
@@ -1099,6 +1104,9 @@
if (maxSdkVersion >= 0) {
printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
}
+ if ((usesPermissionFlags & kNeverForLocation) != 0) {
+ printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
+ }
printer->Print("\n");
for (const std::string& requiredFeature : requiredFeatures) {
printer->Print(StringPrintf(" required-feature='%s'\n", requiredFeature.data()));
@@ -1111,6 +1119,9 @@
if (maxSdkVersion >= 0) {
printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
}
+ if ((usesPermissionFlags & kNeverForLocation) != 0) {
+ printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
+ }
printer->Print("\n");
}
}
@@ -1121,6 +1132,9 @@
if (maxSdkVersion >= 0) {
printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
}
+ if ((usesPermissionFlags & kNeverForLocation) != 0) {
+ printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
+ }
printer->Print(StringPrintf(" reason='%s'\n", reason.data()));
}
};