diff options
91 files changed, 1444 insertions, 1004 deletions
diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java index 8633c9613138..8139a2e963c5 100644 --- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java @@ -30,6 +30,7 @@ import android.util.MergedConfiguration; import android.view.DisplayCutout; import android.view.IWindow; import android.view.IWindowSession; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.View; @@ -120,6 +121,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase { new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration(); final InsetsState mOutInsetsState = new InsetsState(); + final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0]; final IWindow mWindow; final View mView; final WindowManager.LayoutParams mParams; @@ -152,7 +154,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase { mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame, mOutContentInsets, mOutVisibleInsets, mOutStableInsets, mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration, - mOutSurfaceControl, mOutInsetsState, mOutSurfaceSize, + mOutSurfaceControl, mOutInsetsState, mOutControls, mOutSurfaceSize, mOutBlastSurfaceControl); } } diff --git a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java index 4ac3adfd19ce..c72cc9d635e0 100644 --- a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java +++ b/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java @@ -29,6 +29,7 @@ import android.view.Display; import android.view.DisplayCutout; import android.view.IWindowSession; import android.view.InputChannel; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.View; import android.view.WindowManager; @@ -91,6 +92,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase final DisplayCutout.ParcelableWrapper mOutDisplayCutout = new DisplayCutout.ParcelableWrapper(); final InsetsState mOutInsetsState = new InsetsState(); + final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0]; TestWindow() { mLayoutParams.setTitle(TestWindow.class.getName()); @@ -109,7 +111,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase long startTime = SystemClock.elapsedRealtimeNanos(); session.addToDisplay(this, mSeq, mLayoutParams, View.VISIBLE, Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets, - mOutDisplayCutout, inputChannel, mOutInsetsState); + mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls); final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime; state.addExtraResult("add", elapsedTimeNsOfAdd); diff --git a/api/test-current.txt b/api/test-current.txt index 629ce4e6470e..5de6c2ae4c82 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4814,7 +4814,14 @@ package android.view { public final class Display { method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut(); + method public int getType(); method public boolean hasAccess(int); + field public static final int TYPE_EXTERNAL = 2; // 0x2 + field public static final int TYPE_INTERNAL = 1; // 0x1 + field public static final int TYPE_OVERLAY = 4; // 0x4 + field public static final int TYPE_UNKNOWN = 0; // 0x0 + field public static final int TYPE_VIRTUAL = 5; // 0x5 + field public static final int TYPE_WIFI = 3; // 0x3 } public class FocusFinder { @@ -4852,6 +4859,11 @@ package android.view { method public abstract String asyncImpl() default ""; } + public final class SurfaceControl implements android.os.Parcelable { + method public static long acquireFrameRateFlexibilityToken(); + method public static void releaseFrameRateFlexibilityToken(long); + } + public class SurfaceControlViewHost { method public void relayout(android.view.WindowManager.LayoutParams); method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams); diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java index 2a7cfd306174..d5da0b42402c 100644 --- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java +++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java @@ -68,6 +68,7 @@ public class TestDrive { }; private static final String[] DEFAULT_PULL_SOURCES = { "AID_SYSTEM", + "AID_RADIO" }; private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName()); diff --git a/core/java/android/annotation/NonNull.java b/core/java/android/annotation/NonNull.java index a95bf3b8061e..c5aff9d6794e 100644 --- a/core/java/android/annotation/NonNull.java +++ b/core/java/android/annotation/NonNull.java @@ -30,8 +30,8 @@ import java.lang.annotation.Target; * <p> * This is a marker annotation and it has no specific attributes. * - * @paramDoc This value must never be {@code null}. - * @returnDoc This value will never be {@code null}. + * @paramDoc This value cannot be {@code null}. + * @returnDoc This value cannot be {@code null}. * @hide */ @Retention(SOURCE) diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index e476993f003f..b7ceb6ae1b4c 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -299,7 +299,6 @@ interface IActivityTaskManager { in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations); void suppressResizeConfigChanges(boolean suppress); - void moveTasksToFullscreenStack(int fromStackId, boolean onTop); boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds); boolean isInMultiWindowMode(in IBinder token); boolean isInPictureInPictureMode(in IBinder token); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index f216db6fc717..fc48e7f18f5f 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -979,17 +979,14 @@ public final class BluetoothAdapter { 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { @Override protected Integer recompute(Void query) { + // This function must be called while holding the + // mServiceLock, and with mService not null. The public + // getState() method makes this guarantee. try { - mServiceLock.readLock().lock(); - if (mService != null) { - return mService.getState(); - } + return mService.getState(); } catch (RemoteException e) { - Log.e(TAG, "", e); - } finally { - mServiceLock.readLock().unlock(); + throw e.rethrowFromSystemServer(); } - return BluetoothAdapter.STATE_OFF; } }; @@ -1016,7 +1013,24 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { - int state = mBluetoothGetStateCache.query(null); + int state = BluetoothAdapter.STATE_OFF; + + try { + mServiceLock.readLock().lock(); + // The test for mService must either be outside the cache, or + // the cache must be invalidated when mService changes. + if (mService != null) { + state = mBluetoothGetStateCache.query(null); + } + } catch (RuntimeException e) { + if (e.getCause() instanceof RemoteException) { + Log.e(TAG, "", e.getCause()); + } else { + throw e; + } + } finally { + mServiceLock.readLock().unlock(); + } // Consider all internal states as OFF if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index f531e12b0748..f944dd78dc3d 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -55,6 +55,7 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputEventReceiver; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -190,6 +191,7 @@ public abstract class WallpaperService extends Service { new DisplayCutout.ParcelableWrapper(); DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT; final InsetsState mInsetsState = new InsetsState(); + final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0]; final MergedConfiguration mMergedConfiguration = new MergedConfiguration(); private final Point mSurfaceSize = new Point(); @@ -878,7 +880,7 @@ public abstract class WallpaperService extends Service { if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE, mDisplay.getDisplayId(), mWinFrame, mContentInsets, mStableInsets, mDisplayCutout, inputChannel, - mInsetsState) < 0) { + mInsetsState, mTempControls) < 0) { Log.w(TAG, "Failed to add window while updating wallpaper surface."); return; } @@ -903,7 +905,7 @@ public abstract class WallpaperService extends Service { View.VISIBLE, 0, -1, mWinFrame, mContentInsets, mVisibleInsets, mStableInsets, mBackdropFrame, mDisplayCutout, mMergedConfiguration, mSurfaceControl, - mInsetsState, mSurfaceSize, mTmpSurfaceControl); + mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl); if (mSurfaceControl.isValid()) { mSurfaceHolder.mSurface.copyFrom(mSurfaceControl); mSurfaceControl.release(); diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 0ccb1e055c46..4469fdbb12ec 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -253,12 +253,14 @@ public final class Display { * @hide */ @UnsupportedAppUsage + @TestApi public static final int TYPE_UNKNOWN = 0; /** * Display type: Physical display connected through an internal port. * @hide */ + @TestApi public static final int TYPE_INTERNAL = 1; /** @@ -266,6 +268,7 @@ public final class Display { * @hide */ @UnsupportedAppUsage + @TestApi public static final int TYPE_EXTERNAL = 2; /** @@ -273,12 +276,14 @@ public final class Display { * @hide */ @UnsupportedAppUsage + @TestApi public static final int TYPE_WIFI = 3; /** * Display type: Overlay display. * @hide */ + @TestApi public static final int TYPE_OVERLAY = 4; /** @@ -286,6 +291,7 @@ public final class Display { * @hide */ @UnsupportedAppUsage + @TestApi public static final int TYPE_VIRTUAL = 5; /** @@ -569,6 +575,7 @@ public final class Display { * @hide */ @UnsupportedAppUsage + @TestApi public int getType() { return mType; } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 45e51f756489..81bfcb07ab6d 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -29,6 +29,7 @@ import android.view.IWindow; import android.view.IWindowId; import android.view.MotionEvent; import android.view.WindowManager; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.Surface; import android.view.SurfaceControl; @@ -46,7 +47,7 @@ interface IWindowSession { in int viewVisibility, in int layerStackId, out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets, out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel, - out InsetsState insetsState); + out InsetsState insetsState, out InsetsSourceControl[] activeControls); int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outContentInsets, out Rect outStableInsets, out InsetsState insetsState); @@ -106,8 +107,8 @@ interface IWindowSession { out Rect outBackdropFrame, out DisplayCutout.ParcelableWrapper displayCutout, out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl, - out InsetsState insetsState, out Point outSurfaceSize, - out SurfaceControl outBlastSurfaceControl); + out InsetsState insetsState, out InsetsSourceControl[] activeControls, + out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl); /* * Notify the window manager that an application is relaunching and diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index a37c1cbc76ad..b5f9df72f756 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -32,6 +32,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Bitmap; import android.graphics.ColorSpace; @@ -216,6 +217,9 @@ public final class SurfaceControl implements Parcelable { private static native void nativeSetFrameRate( long transactionObj, long nativeObject, float frameRate, int compatibility); + private static native long nativeAcquireFrameRateFlexibilityToken(); + private static native void nativeReleaseFrameRateFlexibilityToken(long token); + private final CloseGuard mCloseGuard = CloseGuard.get(); private String mName; /** @@ -2868,4 +2872,24 @@ public final class SurfaceControl implements Parcelable { } } } + + /** + * Acquire a frame rate flexibility token, which allows surface flinger to freely switch display + * frame rates. This is used by CTS tests to put the device in a consistent state. See + * ISurfaceComposer::acquireFrameRateFlexibilityToken(). + * @hide + */ + @TestApi + public static long acquireFrameRateFlexibilityToken() { + return nativeAcquireFrameRateFlexibilityToken(); + } + + /** + * Release a frame rate flexibility token. + * @hide + */ + @TestApi + public static void releaseFrameRateFlexibilityToken(long token) { + nativeReleaseFrameRateFlexibilityToken(token); + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1e96a1c21ac3..750b1eddd222 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -21,6 +21,7 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.InputDevice.SOURCE_CLASS_NONE; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.InsetsState.LAST_TYPE; import static android.view.View.PFLAG_DRAW_ANIMATION; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; @@ -558,7 +559,8 @@ public final class ViewRootImpl implements ViewParent, final DisplayCutout.ParcelableWrapper mPendingDisplayCutout = new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT); boolean mPendingAlwaysConsumeSystemBars; - private InsetsState mTempInsets = new InsetsState(); + private final InsetsState mTempInsets = new InsetsState(); + private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[LAST_TYPE + 1]; final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); @@ -1003,7 +1005,7 @@ public final class ViewRootImpl implements ViewParent, getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mDisplayCutout, inputChannel, - mTempInsets); + mTempInsets, mTempControls); setFrame(mTmpFrame); } catch (RemoteException e) { mAdded = false; @@ -1028,6 +1030,7 @@ public final class ViewRootImpl implements ViewParent, (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0; mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars; mInsetsController.onStateChanged(mTempInsets); + mInsetsController.onControlsChanged(mTempControls); if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { mAttachInfo.mRootView = null; @@ -7356,7 +7359,7 @@ public final class ViewRootImpl implements ViewParent, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mTmpFrame, mTmpRect, mTmpRect, mTmpRect, mPendingBackDropFrame, mPendingDisplayCutout, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, - mSurfaceSize, mBlastSurfaceControl); + mTempControls, mSurfaceSize, mBlastSurfaceControl); if (mSurfaceControl.isValid()) { if (!mUseBLASTAdapter) { mSurface.copyFrom(mSurfaceControl); @@ -7386,6 +7389,7 @@ public final class ViewRootImpl implements ViewParent, } setFrame(mTmpFrame); mInsetsController.onStateChanged(mTempInsets); + mInsetsController.onControlsChanged(mTempControls); return relayoutResult; } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 144f8e3a7108..39ed4018c65c 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -104,7 +104,7 @@ public class WindowlessWindowManager implements IWindowSession { int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, - InsetsState outInsetsState) { + InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession) .setParent(mRootSurface) .setFormat(attrs.format) @@ -179,7 +179,8 @@ public class WindowlessWindowManager implements IWindowSession { Rect outStableInsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, - Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) { + InsetsSourceControl[] outActiveControls, Point outSurfaceSize, + SurfaceControl outBLASTSurfaceControl) { final State state; synchronized (this) { state = mStateForWindow.get(window.asBinder()); diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java index 71ccb595278b..849488d42bcf 100644 --- a/core/java/android/widget/RadioGroup.java +++ b/core/java/android/widget/RadioGroup.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -484,21 +485,21 @@ public class RadioGroup extends LinearLayout { super.onInitializeAccessibilityNodeInfo(info); if (this.getOrientation() == HORIZONTAL) { info.setCollectionInfo(AccessibilityNodeInfo.CollectionInfo.obtain(1, - getVisibleChildCount(), false, + getVisibleChildWithTextCount(), false, AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE)); } else { info.setCollectionInfo( - AccessibilityNodeInfo.CollectionInfo.obtain(getVisibleChildCount(), + AccessibilityNodeInfo.CollectionInfo.obtain(getVisibleChildWithTextCount(), 1, false, AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_SINGLE)); } } - private int getVisibleChildCount() { + private int getVisibleChildWithTextCount() { int count = 0; for (int i = 0; i < getChildCount(); i++) { if (this.getChildAt(i) instanceof RadioButton) { - if (((RadioButton) this.getChildAt(i)).getVisibility() == VISIBLE) { + if (isVisibleWithText((RadioButton) this.getChildAt(i))) { count++; } } @@ -513,15 +514,19 @@ public class RadioGroup extends LinearLayout { int index = 0; for (int i = 0; i < getChildCount(); i++) { if (this.getChildAt(i) instanceof RadioButton) { - RadioButton radioButton = (RadioButton) this.getChildAt(i); - if (radioButton == child) { + RadioButton button = (RadioButton) this.getChildAt(i); + if (button == child) { return index; } - if (radioButton.getVisibility() == VISIBLE) { + if (isVisibleWithText(button)) { index++; } } } return -1; } + + private boolean isVisibleWithText(RadioButton button) { + return button.getVisibility() == VISIBLE && !TextUtils.isEmpty(button.getText()); + } }
\ No newline at end of file diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java index 9fc0da83c504..d43333e507a6 100644 --- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java @@ -55,6 +55,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { private static final String TAG = "AbstractMultiProfilePagerAdapter"; static final int PROFILE_PERSONAL = 0; static final int PROFILE_WORK = 1; + @IntDef({PROFILE_PERSONAL, PROFILE_WORK}) @interface Profile {} @@ -365,7 +366,7 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { UserHandle listUserHandle = listAdapter.getUserHandle(); if (!listUserHandle.equals(mWorkProfileUserHandle) || !mInjector.isQuietModeEnabled(mWorkProfileUserHandle) - || !hasResolvedAppsInWorkProfile(listAdapter)) { + || listAdapter.getCount() == 0) { return false; } DevicePolicyEventLogger @@ -382,20 +383,6 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { return true; } - /** - * Returns {@code true} if there is at least one app resolved in the work profile, - * regardless of whether the work profile is enabled or not. - */ - private boolean hasResolvedAppsInWorkProfile(ResolverListAdapter listAdapter) { - List<ResolverActivity.ResolvedComponentInfo> userStateIndependentWorkResolvers = - listAdapter.mResolverListController.getUserStateIndependentResolversAsUser( - listAdapter.getIntents(), mWorkProfileUserHandle); - return userStateIndependentWorkResolvers.stream() - .anyMatch(resolvedComponentInfo -> - resolvedComponentInfo.getResolveInfoAt(0).targetUserId - == UserHandle.USER_CURRENT); - } - private void maybeShowNoAppsAvailableEmptyState(ResolverListAdapter listAdapter) { UserHandle listUserHandle = listAdapter.getUserHandle(); if (mWorkProfileUserHandle != null @@ -529,6 +516,13 @@ public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter { return false; } + boolean shouldShowEmptyStateScreen(ResolverListAdapter listAdapter) { + int count = listAdapter.getUnfilteredCount(); + return (count == 0 && listAdapter.getPlaceholderCount() == 0) + || (listAdapter.getUserHandle().equals(mWorkProfileUserHandle) + && isQuietModeEnabled(mWorkProfileUserHandle)); + } + protected class ProfileDescriptor { final ViewGroup rootView; private final ViewGroup mEmptyStateView; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 35253b68aac7..fc3f20f7a556 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -994,7 +994,7 @@ public class ResolverActivity extends Activity implements if (isAutolaunching() || maybeAutolaunchActivity()) { return; } - if (isResolverListEmpty(listAdapter)) { + if (mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(listAdapter)) { mMultiProfilePagerAdapter.showEmptyResolverListEmptyState(listAdapter); } else { mMultiProfilePagerAdapter.showListView(listAdapter); @@ -1641,16 +1641,11 @@ public class ResolverActivity extends Activity implements private void setupViewVisibilities() { ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter(); - if (!isResolverListEmpty(activeListAdapter)) { + if (!mMultiProfilePagerAdapter.shouldShowEmptyStateScreen(activeListAdapter)) { addUseDifferentAppLabelIfNecessary(activeListAdapter); } } - private boolean isResolverListEmpty(ResolverListAdapter listAdapter) { - int count = listAdapter.getUnfilteredCount(); - return count == 0 && listAdapter.getPlaceholderCount() == 0; - } - /** * Add a label to signify that the user can pick a different app. * @param adapter The adapter used to provide data to item views. diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 3f897a5f26bf..033ac72dda4e 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -121,23 +121,13 @@ public class ResolverListController { List<Intent> intents, UserHandle userHandle) { int baseFlags = PackageManager.MATCH_DEFAULT_ONLY + | PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0); return getResolversForIntentAsUserInternal(intents, userHandle, baseFlags); } - /** - * Returns a list of resolved intents which is user state-independent. This means it will - * return the same results regardless of whether the {@code userHandle} user is disabled or not. - */ - public List<ResolverActivity.ResolvedComponentInfo> getUserStateIndependentResolversAsUser( - List<Intent> intents, - UserHandle userHandle) { - int baseFlags = PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; - return getResolversForIntentAsUserInternal(intents, userHandle, baseFlags); - } - private List<ResolverActivity.ResolvedComponentInfo> getResolversForIntentAsUserInternal( List<Intent> intents, UserHandle userHandle, diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 5a979ac97c54..26684201019c 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -40,7 +40,6 @@ import android.os.Bundle; import android.os.Parcelable; import android.text.TextUtils; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; @@ -387,14 +386,17 @@ public class ConversationLayout extends FrameLayout /** @hide */ public void setUnreadCount(int unreadCount) { - mUnreadBadge.setVisibility(mIsCollapsed && unreadCount > 1 ? VISIBLE : GONE); - CharSequence text = unreadCount >= 100 - ? getResources().getString(R.string.unread_convo_overflow, 99) - : String.format(Locale.getDefault(), "%d", unreadCount); - mUnreadBadge.setText(text); - mUnreadBadge.setBackgroundTintList(ColorStateList.valueOf(mLayoutColor)); - boolean needDarkText = ColorUtils.calculateLuminance(mLayoutColor) > 0.5f; - mUnreadBadge.setTextColor(needDarkText ? Color.BLACK : Color.WHITE); + boolean visible = mIsCollapsed && unreadCount > 1; + mUnreadBadge.setVisibility(visible ? VISIBLE : GONE); + if (visible) { + CharSequence text = unreadCount >= 100 + ? getResources().getString(R.string.unread_convo_overflow, 99) + : String.format(Locale.getDefault(), "%d", unreadCount); + mUnreadBadge.setText(text); + mUnreadBadge.setBackgroundTintList(ColorStateList.valueOf(mLayoutColor)); + boolean needDarkText = ColorUtils.calculateLuminance(mLayoutColor) > 0.5f; + mUnreadBadge.setTextColor(needDarkText ? Color.BLACK : Color.WHITE); + } } private void addRemoteInputHistoryToMessages( @@ -537,37 +539,26 @@ public class ConversationLayout extends FrameLayout } private void updateImageMessages() { - boolean displayExternalImage = false; - ArraySet<View> newMessages = new ArraySet<>(); - if (mIsCollapsed) { - - // When collapsed, we're displaying all image messages in a dedicated container - // on the right of the layout instead of inline. Let's add all isolated images there - int imageIndex = 0; - for (int i = 0; i < mGroups.size(); i++) { - MessagingGroup messagingGroup = mGroups.get(i); - MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage(); - if (isolatedMessage != null) { - newMessages.add(isolatedMessage.getView()); - displayExternalImage = true; - if (imageIndex - != mImageMessageContainer.indexOfChild(isolatedMessage.getView())) { - mImageMessageContainer.removeView(isolatedMessage.getView()); - mImageMessageContainer.addView(isolatedMessage.getView(), imageIndex); - } - imageIndex++; - } + View newMessage = null; + if (mIsCollapsed && mGroups.size() > 0) { + + // When collapsed, we're displaying the image message in a dedicated container + // on the right of the layout instead of inline. Let's add the isolated image there + MessagingGroup messagingGroup = mGroups.get(mGroups.size() -1); + MessagingImageMessage isolatedMessage = messagingGroup.getIsolatedMessage(); + if (isolatedMessage != null) { + newMessage = isolatedMessage.getView(); } } // Remove all messages that don't belong into the image layout - for (int i = 0; i < mImageMessageContainer.getChildCount(); i++) { - View child = mImageMessageContainer.getChildAt(i); - if (!newMessages.contains(child)) { - mImageMessageContainer.removeView(child); - i--; + View previousMessage = mImageMessageContainer.getChildAt(0); + if (previousMessage != newMessage) { + mImageMessageContainer.removeView(previousMessage); + if (newMessage != null) { + mImageMessageContainer.addView(newMessage); } } - mImageMessageContainer.setVisibility(displayExternalImage ? VISIBLE : GONE); + mImageMessageContainer.setVisibility(newMessage != null ? VISIBLE : GONE); } private void bindFacePile() { diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index b2ca0a7bcbe3..b11c4c92ff33 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -29,11 +29,13 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_view_Surface.h> #include <android_runtime/android_view_SurfaceSession.h> +#include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <jni.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedUtfChars.h> +#include <private/gui/ComposerService.h> #include <stdio.h> #include <system/graphics.h> #include <ui/ConfigStoreTypes.h> @@ -624,6 +626,23 @@ static void nativeSetFrameRate(JNIEnv* env, jclass clazz, jlong transactionObj, transaction->setFrameRate(ctrl, frameRate, static_cast<int8_t>(compatibility)); } +static jlong nativeAcquireFrameRateFlexibilityToken(JNIEnv* env, jclass clazz) { + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<IBinder> token; + status_t result = composer->acquireFrameRateFlexibilityToken(&token); + if (result < 0) { + ALOGE("Failed acquiring frame rate flexibility token: %s (%d)", strerror(-result), result); + return 0; + } + token->incStrong((void*)nativeAcquireFrameRateFlexibilityToken); + return reinterpret_cast<jlong>(token.get()); +} + +static void nativeReleaseFrameRateFlexibilityToken(JNIEnv* env, jclass clazz, jlong tokenLong) { + sp<IBinder> token(reinterpret_cast<IBinder*>(tokenLong)); + token->decStrong((void*)nativeAcquireFrameRateFlexibilityToken); +} + static jlongArray nativeGetPhysicalDisplayIds(JNIEnv* env, jclass clazz) { const auto displayIds = SurfaceComposerClient::getPhysicalDisplayIds(); jlongArray array = env->NewLongArray(displayIds.size()); @@ -1474,6 +1493,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetShadowRadius }, {"nativeSetFrameRate", "(JJFI)V", (void*)nativeSetFrameRate }, + {"nativeAcquireFrameRateFlexibilityToken", "()J", + (void*)nativeAcquireFrameRateFlexibilityToken }, + {"nativeReleaseFrameRateFlexibilityToken", "(J)V", + (void*)nativeReleaseFrameRateFlexibilityToken }, {"nativeGetPhysicalDisplayIds", "()[J", (void*)nativeGetPhysicalDisplayIds }, {"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;", diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml index a41d717a83bd..5890bed11b07 100644 --- a/core/res/res/layout/resolver_empty_states.xml +++ b/core/res/res/layout/resolver_empty_states.xml @@ -19,62 +19,66 @@ android:id="@+id/resolver_empty_state" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" android:gravity="center_horizontal" android:visibility="gone" - android:paddingTop="48dp" - android:paddingBottom="48dp" android:paddingStart="24dp" android:paddingEnd="24dp"> - <ImageView - android:id="@+id/resolver_empty_state_icon" - android:layout_width="24dp" - android:layout_height="24dp" - android:layout_centerHorizontal="true" /> - <TextView - android:id="@+id/resolver_empty_state_title" - android:layout_below="@+id/resolver_empty_state_icon" - android:layout_marginTop="8dp" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:fontFamily="@string/config_headlineFontFamilyMedium" - android:textColor="@color/resolver_empty_state_text" - android:textSize="14sp" - android:layout_centerHorizontal="true" /> - <TextView - android:id="@+id/resolver_empty_state_subtitle" - android:layout_below="@+id/resolver_empty_state_title" - android:layout_marginTop="4dp" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@color/resolver_empty_state_text" - android:textSize="12sp" - android:gravity="center_horizontal" - android:layout_centerHorizontal="true" /> - <Button - android:id="@+id/resolver_empty_state_button" - android:layout_below="@+id/resolver_empty_state_subtitle" - android:layout_marginTop="16dp" - android:text="@string/resolver_switch_on_work" - android:layout_width="wrap_content" + <RelativeLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:fontFamily="@string/config_headlineFontFamilyMedium" - android:textSize="14sp" - android:textColor="?attr/colorAccent" - android:layout_centerHorizontal="true" - android:background="@drawable/resolver_turn_on_work_button_ripple_background"/> - <ProgressBar - android:id="@+id/resolver_empty_state_progress" - style="@style/Widget.Material.Light.ProgressBar" - android:visibility="gone" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:indeterminate="true" - android:layout_alignTop="@+id/resolver_empty_state_icon" - android:layout_alignBottom="@+id/resolver_empty_state_button" - android:layout_centerHorizontal="true" - android:layout_below="@+id/resolver_empty_state_subtitle" - android:indeterminateTint="?attr/colorAccent"/> + android:paddingTop="48dp" + android:paddingBottom="48dp" + android:gravity="center_horizontal"> + <ImageView + android:id="@+id/resolver_empty_state_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_centerHorizontal="true" /> + <TextView + android:id="@+id/resolver_empty_state_title" + android:layout_below="@+id/resolver_empty_state_icon" + android:layout_marginTop="8dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@string/config_headlineFontFamilyMedium" + android:textColor="@color/resolver_empty_state_text" + android:textSize="14sp" + android:layout_centerHorizontal="true" /> + <TextView + android:id="@+id/resolver_empty_state_subtitle" + android:layout_below="@+id/resolver_empty_state_title" + android:layout_marginTop="4dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@color/resolver_empty_state_text" + android:textSize="12sp" + android:gravity="center_horizontal" + android:layout_centerHorizontal="true" /> + <Button + android:id="@+id/resolver_empty_state_button" + android:layout_below="@+id/resolver_empty_state_subtitle" + android:layout_marginTop="16dp" + android:text="@string/resolver_switch_on_work" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@string/config_headlineFontFamilyMedium" + android:textSize="14sp" + android:textColor="?attr/colorAccent" + android:layout_centerHorizontal="true" + android:background="@drawable/resolver_turn_on_work_button_ripple_background"/> + <ProgressBar + android:id="@+id/resolver_empty_state_progress" + style="@style/Widget.Material.Light.ProgressBar" + android:visibility="gone" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:indeterminate="true" + android:layout_alignTop="@+id/resolver_empty_state_icon" + android:layout_alignBottom="@+id/resolver_empty_state_button" + android:layout_centerHorizontal="true" + android:layout_below="@+id/resolver_empty_state_subtitle" + android:indeterminateTint="?attr/colorAccent"/> + </RelativeLayout> <TextView android:id="@+id/empty" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index bc0cdc1e029b..583c75102d52 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -1339,15 +1339,8 @@ public class ChooserActivityTest { createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(workProfileTargets); - when(sOverrides.workResolverListController.getUserStateIndependentResolversAsUser( - Mockito.isA(List.class), - Mockito.isA(UserHandle.class))) - .thenReturn(new ArrayList<>(workResolvedComponentInfos)); sOverrides.isQuietModeEnabled = true; - // When work profile is disabled, we get 0 results when we query the work profile - // intents. - setupResolverControllers(personalResolvedComponentInfos, - /* workResolvedComponentInfos */ new ArrayList<>()); + setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); Intent sendIntent = createSendTextIntent(); sendIntent.setType("TestType"); diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 0bf8663c7a85..eb39d58019d9 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -614,15 +614,8 @@ public class ResolverActivityTest { createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(workProfileTargets); - when(sOverrides.workResolverListController.getUserStateIndependentResolversAsUser( - Mockito.isA(List.class), - Mockito.isA(UserHandle.class))) - .thenReturn(new ArrayList<>(workResolvedComponentInfos)); sOverrides.isQuietModeEnabled = true; - // When work profile is disabled, we get 0 results when we query the work profile - // intents. - setupResolverControllers(personalResolvedComponentInfos, - /* workResolvedComponentInfos */ new ArrayList<>()); + setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos); Intent sendIntent = createSendImageIntent(); sendIntent.setType("TestType"); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 41aa1ff80e3c..5088494d6a07 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -418,9 +418,9 @@ void SkiaPipeline::endCapture(SkSurface* surface) { auto data = picture->serialize(); savePictureAsync(data, mCapturedFile); mCaptureSequence = 0; + mCaptureMode = CaptureMode::None; } } - mCaptureMode = CaptureMode::None; mRecorder.reset(); } } diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 90bcd1c0e370..1208062d9da0 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -403,3 +403,40 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); } + +RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { + // create a pipeline and add a picture callback + auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); + int callbackCount = 0; + pipeline->setPictureCapturedCallback( + [&callbackCount](sk_sp<SkPicture>&& picture) { callbackCount += 1; }); + + // create basic red frame and render it + auto redNode = TestUtils::createSkiaNode( + 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { + redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); + }); + LayerUpdateQueue layerUpdateQueue; + SkRect dirty = SkRectMakeLargest(); + std::vector<sp<RenderNode>> renderNodes; + renderNodes.push_back(redNode); + bool opaque = true; + android::uirenderer::Rect contentDrawBounds(0, 0, 1, 1); + auto surface = SkSurface::MakeRasterN32Premul(1, 1); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + + // verify the callback was called + EXPECT_EQ(1, callbackCount); + + // render a second frame and check the callback count + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + EXPECT_EQ(2, callbackCount); + + // unset the callback, render another frame, check callback was not invoked + pipeline->setPictureCapturedCallback(nullptr); + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, + SkMatrix::I()); + EXPECT_EQ(2, callbackCount); +} diff --git a/media/java/android/media/MediaMetrics.java b/media/java/android/media/MediaMetrics.java index 88a829546989..540955f3b393 100644 --- a/media/java/android/media/MediaMetrics.java +++ b/media/java/android/media/MediaMetrics.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.os.Bundle; @@ -24,6 +25,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.Objects; /** * MediaMetrics is the Java interface to the MediaMetrics service. @@ -50,6 +52,77 @@ public class MediaMetrics { private static final Charset MEDIAMETRICS_CHARSET = StandardCharsets.UTF_8; /** + * Key interface. + * + * The presence of this {@code Key} interface on an object allows + * it to be used to set metrics. + * + * @param <T> type of value associated with {@code Key}. + */ + public interface Key<T> { + /** + * Returns the internal name of the key. + */ + @NonNull + String getName(); + + /** + * Returns the class type of the associated value. + */ + @NonNull + Class<T> getValueClass(); + } + + /** + * Returns a Key object with the correct interface for MediaMetrics. + * + * @param name The name of the key. + * @param type The class type of the value represented by the key. + * @param <T> The type of value. + * @return a new key interface. + */ + @NonNull + public static <T> Key<T> createKey(@NonNull String name, @NonNull Class<T> type) { + // Implementation specific. + return new Key<T>() { + private final String mName = name; + private final Class<T> mType = type; + + @Override + @NonNull + public String getName() { + return mName; + } + + @Override + @NonNull + public Class<T> getValueClass() { + return mType; + } + + /** + * Return true if the name and the type of two objects are the same. + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Key)) { + return false; + } + Key<?> other = (Key<?>) obj; + return mName.equals(other.getName()) && mType.equals(other.getValueClass()); + } + + @Override + public int hashCode() { + return Objects.hash(mName, mType); + } + }; + } + + /** * Item records properties and delivers to the MediaMetrics service * */ @@ -202,6 +275,28 @@ public class MediaMetrics { } /** + * Sets a metrics typed key + * @param key + * @param value + * @param <T> + * @return + */ + @NonNull + public <T> Item set(@NonNull Key<T> key, @Nullable T value) { + if (value instanceof Integer) { + putInt(key.getName(), (int) value); + } else if (value instanceof Long) { + putLong(key.getName(), (long) value); + } else if (value instanceof Double) { + putDouble(key.getName(), (double) value); + } else if (value instanceof String) { + putString(key.getName(), (String) value); + } + // if value is null, etc. no error is raised. + return this; + } + + /** * Sets the property with key to an integer (32 bit) value. * * @param key diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 07f4a9a799ad..53e7921879d1 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -566,7 +566,7 @@ <!-- [CHAR LIMIT=50] Title for adb wireless pair by QR code preference --> <string name="adb_pair_method_qrcode_title">Pair device with QR code</string> <!-- [CHAR LIMIT=NONE] Summary for adb wireless pair by QR code preference --> - <string name="adb_pair_method_qrcode_summary">Pair new devices using QR code Scanner</string> + <string name="adb_pair_method_qrcode_summary">Pair new devices using QR code scanner</string> <!-- [CHAR LIMIT=50] Title for adb wireless pair by pairing code preference --> <string name="adb_pair_method_code_title">Pair device with pairing code</string> <!-- [CHAR LIMIT=NONE] Summary for adb wireless pair by pairing code preference --> @@ -604,7 +604,7 @@ <!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing scanner title --> <string name="adb_wireless_qrcode_pairing_title">Scan QR code</string> <!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing description --> - <string name="adb_wireless_qrcode_pairing_description">Pair device over Wi\u2011Fi by scanning a QR Code</string> + <string name="adb_wireless_qrcode_pairing_description">Pair device over Wi\u2011Fi by scanning a QR code</string> <!-- [CHAR LIMIT=NONE] Toast message when trying to enable Wi-Fi debugging and no Wi-Fi network connected --> <string name="adb_wireless_no_network_msg">Please connect to a Wi\u2011Fi network</string> <!--Adb wireless search Keywords [CHAR LIMIT=NONE]--> diff --git a/packages/SystemUI/res/layout/bubble_dismiss_target.xml b/packages/SystemUI/res/layout/bubble_dismiss_target.xml index ca085b69c35d..f5cd727a6d03 100644 --- a/packages/SystemUI/res/layout/bubble_dismiss_target.xml +++ b/packages/SystemUI/res/layout/bubble_dismiss_target.xml @@ -17,7 +17,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" - android:layout_height="@dimen/pip_dismiss_gradient_height" + android:layout_height="@dimen/floating_dismiss_gradient_height" android:layout_gravity="bottom|center_horizontal"> <FrameLayout diff --git a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml index 50aa212c94a6..cb53fe619b24 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_item_v2.xml @@ -27,7 +27,7 @@ android:paddingRight="@dimen/global_actions_grid_item_side_margin" android:layout_marginRight="3dp" android:layout_marginLeft="3dp" - android:background="@drawable/rounded_bg_full"> + android:background="@drawable/control_background"> <LinearLayout android:layout_width="@dimen/global_actions_grid_item_width" android:layout_height="@dimen/global_actions_grid_item_height" @@ -42,7 +42,7 @@ android:layout_marginLeft="@dimen/global_actions_grid_item_icon_side_margin" android:layout_marginRight="@dimen/global_actions_grid_item_icon_side_margin" android:scaleType="centerInside" - android:tint="@color/global_actions_text" /> + android:tint="@color/control_default_foreground" /> <TextView android:id="@*android:id/message" @@ -53,7 +53,7 @@ android:singleLine="true" android:gravity="center" android:textSize="12dp" - android:textColor="@color/global_actions_text" + android:textColor="@color/control_default_foreground" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView @@ -62,7 +62,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" - android:textColor="@color/global_actions_text" + android:textColor="@color/control_default_foreground" android:textAppearance="?android:attr/textAppearanceSmall" /> </LinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/pip_dismiss_view.xml b/packages/SystemUI/res/layout/pip_dismiss_view.xml deleted file mode 100644 index 2cc4b220fe2b..000000000000 --- a/packages/SystemUI/res/layout/pip_dismiss_view.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2016 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. ---> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="@dimen/pip_dismiss_gradient_height" - android:alpha="0"> - - <TextView - android:id="@+id/pip_dismiss_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="bottom|center_horizontal" - android:text="@string/pip_phone_dismiss_hint" - android:textColor="#FFFFFFFF" - android:textSize="14sp" - android:shadowColor="@android:color/black" - android:shadowDx="-2" - android:shadowDy="2" - android:shadowRadius="0.01" /> - -</FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9b9fbed0d904..bce5fac76cfc 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -955,7 +955,7 @@ <dimen name="recents_quick_scrub_onboarding_margin_start">8dp</dimen> <!-- The height of the gradient indicating the dismiss edge when moving a PIP. --> - <dimen name="pip_dismiss_gradient_height">176dp</dimen> + <dimen name="floating_dismiss_gradient_height">176dp</dimen> <!-- The bottom margin of the PIP drag to dismiss info text shown when moving a PIP. --> <dimen name="pip_dismiss_text_bottom_margin">24dp</dimen> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index caee8ccb6970..88f4176f5eac 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -34,6 +34,7 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; +import com.android.systemui.Dependency; import com.android.systemui.R; /** @@ -50,6 +51,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout private boolean mDismissing; protected boolean mResumed; private CountDownTimer mCountdownTimer = null; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; // To avoid accidental lockout due to events while the device in in the pocket, ignore // any passwords with length less than or equal to this length. @@ -61,6 +63,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) { super(context, attrs); + mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); } @Override @@ -151,6 +154,8 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); } + + mKeyguardUpdateMonitor.setCredentialAttempted(); mPendingLockCheck = LockPatternChecker.checkCredential( mLockPatternUtils, password, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 48c6bd114d4a..ad92f8f623e4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -282,6 +282,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit @Override public void onPatternDetected(final List<LockPatternView.Cell> pattern) { + mKeyguardUpdateMonitor.setCredentialAttempted(); mLockPatternView.disableInput(); if (mPendingLockCheck != null) { mPendingLockCheck.cancel(false); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 57e3f14d7aed..88d6943b4071 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -223,6 +223,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private int mRingMode; private int mPhoneState; private boolean mKeyguardIsVisible; + private boolean mCredentialAttempted; private boolean mKeyguardGoingAway; private boolean mGoingToSleep; private boolean mBouncer; @@ -498,11 +499,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } /** + * Updates KeyguardUpdateMonitor's internal state to know if credential was attempted on + * bouncer. Note that this does not care if the credential was correct/incorrect. This is + * cleared when the user leaves the bouncer (unlocked, screen off, back to lockscreen, etc) + */ + public void setCredentialAttempted() { + mCredentialAttempted = true; + updateBiometricListeningState(); + } + + /** * Updates KeyguardUpdateMonitor's internal state to know if keyguard is goingAway */ public void setKeyguardGoingAway(boolean goingAway) { mKeyguardGoingAway = goingAway; - updateFingerprintListeningState(); + updateBiometricListeningState(); } /** @@ -664,6 +675,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab updateFingerprintListeningState(); } else { setFingerprintRunningState(BIOMETRIC_STATE_STOPPED); + mFingerprintCancelSignal = null; + mFaceCancelSignal = null; } if (msgId == FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE) { @@ -679,6 +692,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab getCurrentUser()); } + if (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT + || msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) { + mFingerprintLockedOut = true; + } + for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { @@ -688,6 +706,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private void handleFingerprintLockoutReset() { + mFingerprintLockedOut = false; updateFingerprintListeningState(); } @@ -1274,6 +1293,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private CancellationSignal mFaceCancelSignal; private FingerprintManager mFpm; private FaceManager mFaceManager; + private boolean mFingerprintLockedOut; /** * When we receive a @@ -1820,13 +1840,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private boolean shouldListenForFingerprint() { + final boolean allowedOnBouncer = + !(mFingerprintLockedOut && mBouncer && mCredentialAttempted); + // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. final boolean shouldListen = (mKeyguardIsVisible || !mDeviceInteractive || (mBouncer && !mKeyguardGoingAway) || mGoingToSleep || shouldListenForFingerprintAssistant() || (mKeyguardOccluded && mIsDreaming)) && !mSwitchingUser && !isFingerprintDisabled(getCurrentUser()) - && (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser; + && (!mKeyguardGoingAway || !mDeviceInteractive) && mIsPrimaryUser + && allowedOnBouncer; return shouldListen; } @@ -2372,6 +2396,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // camera requests dismiss keyguard (tapping on photos for example). When these happen, // face auth should resume. mSecureCameraLaunched = false; + } else { + mCredentialAttempted = false; } for (int i = 0; i < mCallbacks.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index eff693436451..044feaa117c8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -514,7 +514,7 @@ public class BubbleStackView extends FrameLayout { mDismissTargetContainer = new FrameLayout(context); mDismissTargetContainer.setLayoutParams(new FrameLayout.LayoutParams( MATCH_PARENT, - getResources().getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height), + getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height), Gravity.BOTTOM)); mDismissTargetContainer.setClipChildren(false); mDismissTargetContainer.addView(targetView); @@ -523,7 +523,7 @@ public class BubbleStackView extends FrameLayout { // Start translated down so the target springs up. targetView.setTranslationY( - getResources().getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height)); + getResources().getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height)); // Save the MagneticTarget instance for the newly set up view - we'll add this to the // MagnetizedObjects. diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index f2303e622f8d..fe1e6328820d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -55,13 +55,19 @@ class ControlsFavoritingActivity @Inject constructor( companion object { private const val TAG = "ControlsFavoritingActivity" + + // If provided and no structure is available, use as the title const val EXTRA_APP = "extra_app_label" + + // If provided, show this structure page first + const val EXTRA_STRUCTURE = "extra_structure" private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT private const val TOOLTIP_MAX_SHOWN = 2 } private var component: ComponentName? = null private var appName: CharSequence? = null + private var structureExtra: CharSequence? = null private lateinit var structurePager: ViewPager2 private lateinit var statusText: TextView @@ -111,6 +117,7 @@ class ControlsFavoritingActivity @Inject constructor( val collator = Collator.getInstance(resources.configuration.locales[0]) comparator = compareBy(collator) { it.structureName } appName = intent.getCharSequenceExtra(EXTRA_APP) + structureExtra = intent.getCharSequenceExtra(EXTRA_STRUCTURE) ?: "" component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME) bindViews() @@ -137,9 +144,15 @@ class ControlsFavoritingActivity @Inject constructor( listOfStructures = controlsByStructure.map { StructureContainer(it.key, AllModel(it.value, favoriteKeys, emptyZoneString)) }.sortedWith(comparator) + + val structureIndex = listOfStructures.indexOfFirst { + sc -> sc.structureName == structureExtra + }.let { if (it == -1) 0 else it } + executor.execute { doneButton.isEnabled = true structurePager.adapter = StructureAdapter(listOfStructures) + structurePager.setCurrentItem(structureIndex) if (error) { statusText.text = resources.getText(R.string.controls_favorite_load_error) } else { @@ -247,7 +260,10 @@ class ControlsFavoritingActivity @Inject constructor( requireViewById<Button>(R.id.other_apps).apply { visibility = View.VISIBLE setOnClickListener { - this@ControlsFavoritingActivity.onBackPressed() + val i = Intent() + i.setComponent( + ComponentName(context, ControlsProviderSelectorActivity::class.java)) + context.startActivity(i) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 6e527e29a848..208d9117e088 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -45,6 +45,7 @@ import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.StructureInfo +import com.android.systemui.controls.management.ControlsFavoritingActivity import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.management.ControlsProviderSelectorActivity import com.android.systemui.dagger.qualifiers.Background @@ -92,21 +93,8 @@ class ControlsUiControllerImpl @Inject constructor ( private lateinit var lastItems: List<SelectionItem> private var popup: ListPopupWindow? = null private var activeDialog: Dialog? = null - private val addControlsItem: SelectionItem private var hidden = true - init { - val addDrawable = context.getDrawable(R.drawable.ic_add).apply { - setTint(context.resources.getColor(R.color.control_secondary_text, null)) - } - addControlsItem = SelectionItem( - context.resources.getString(R.string.controls_providers_title), - "", - addDrawable, - EMPTY_COMPONENT - ) - } - override val available: Boolean get() = controlsController.get().available @@ -184,7 +172,7 @@ class ControlsUiControllerImpl @Inject constructor ( inflater.inflate(R.layout.controls_no_favorites, parent, true) val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup - viewGroup.setOnClickListener(launchSelectorActivityListener(context)) + viewGroup.setOnClickListener { v: View -> startProviderSelectorActivity(v.context) } val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle) subtitle.setText(context.resources.getString(R.string.quick_controls_subtitle)) @@ -198,16 +186,28 @@ class ControlsUiControllerImpl @Inject constructor ( } } - private fun launchSelectorActivityListener(context: Context): (View) -> Unit { - return { _ -> - val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) - context.sendBroadcast(closeDialog) + private fun startFavoritingActivity(context: Context, si: StructureInfo) { + val i = Intent(context, ControlsFavoritingActivity::class.java).apply { + putExtra(ControlsFavoritingActivity.EXTRA_APP, + controlsListingController.get().getAppLabel(si.componentName)) + putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, si.structure) + putExtra(Intent.EXTRA_COMPONENT_NAME, si.componentName) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + } + startActivity(context, i) + } - val i = Intent() - i.setComponent(ComponentName(context, ControlsProviderSelectorActivity::class.java)) - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(i) + private fun startProviderSelectorActivity(context: Context) { + val i = Intent(context, ControlsProviderSelectorActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) } + startActivity(context, i) + } + + private fun startActivity(context: Context, intent: Intent) { + val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS) + context.sendBroadcast(closeDialog) + context.startActivity(intent) } private fun showControlsView(items: List<SelectionItem>) { @@ -248,7 +248,7 @@ class ControlsUiControllerImpl @Inject constructor ( ) { when (pos) { // 0: Add Control - 0 -> launchSelectorActivityListener(view.context)(parent) + 0 -> startFavoritingActivity(view.context, selectedStructure) else -> Log.w(ControlsUiController.TAG, "Unsupported index ($pos) on 'more' menu selection") } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index fdd859373685..4dd5e87d2c93 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -489,6 +489,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, mAdapter = new MyAdapter(); + mDepthController.setShowingHomeControls(shouldShowControls()); ActionsDialog dialog = new ActionsDialog(mContext, mAdapter, getWalletPanelViewController(), mDepthController, mSysuiColorExtractor, mStatusBarService, mNotificationShadeWindowController, @@ -1780,8 +1781,12 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } if (mBackgroundDrawable == null) { mBackgroundDrawable = new ScrimDrawable(); - mScrimAlpha = mBlurUtils.supportsBlursOnWindows() - ? ScrimController.BLUR_SCRIM_ALPHA : ScrimController.BUSY_SCRIM_ALPHA; + if (mControlsUiController != null) { + mScrimAlpha = 1.0f; + } else { + mScrimAlpha = mBlurUtils.supportsBlursOnWindows() + ? ScrimController.BLUR_SCRIM_ALPHA : ScrimController.BUSY_SCRIM_ALPHA; + } } getWindow().setBackgroundDrawable(mBackgroundDrawable); } @@ -1841,8 +1846,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (!(mBackgroundDrawable instanceof ScrimDrawable)) { return; } - ((ScrimDrawable) mBackgroundDrawable).setColor(colors.supportsDarkText() ? Color.WHITE - : Color.BLACK, animate); + boolean hasControls = mControlsUiController != null; + ((ScrimDrawable) mBackgroundDrawable).setColor( + !hasControls && colors.supportsDarkText() ? Color.WHITE : Color.BLACK, animate); View decorView = getWindow().getDecorView(); if (colors.supportsDarkText()) { decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR | diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java deleted file mode 100644 index b7258117c48c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2016 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.pip.phone; - -import android.content.Context; -import android.graphics.PixelFormat; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.VibrationEffect; -import android.os.Vibrator; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; -import android.widget.FrameLayout; - -import com.android.systemui.Interpolators; -import com.android.systemui.R; -import com.android.systemui.shared.system.WindowManagerWrapper; - -/** - * Displays the dismiss UI and target for floating objects. - */ -public class PipDismissViewController { - - // This delay controls how long to wait before we show the target when the user first moves - // the PIP, to prevent the target from animating if the user just wants to fling the PIP - public static final int SHOW_TARGET_DELAY = 100; - private static final int SHOW_TARGET_DURATION = 350; - private static final int HIDE_TARGET_DURATION = 225; - - private Context mContext; - private WindowManager mWindowManager; - private View mDismissView; - - // Used for dismissing a bubble -- bubble should be in the target to be considered a dismiss - private View mTargetView; - private int mTargetSlop; - private Point mWindowSize; - private int[] mLoc = new int[2]; - private boolean mIntersecting; - private Vibrator mVibe; - - public PipDismissViewController(Context context) { - mContext = context; - mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - mVibe = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - } - - /** - * Creates the dismiss target for showing via {@link #showDismissTarget()}. - */ - public void createDismissTarget() { - if (mDismissView == null) { - // Determine sizes for the view - final Rect stableInsets = new Rect(); - WindowManagerWrapper.getInstance().getStableInsets(stableInsets); - mWindowSize = new Point(); - mWindowManager.getDefaultDisplay().getRealSize(mWindowSize); - final int gradientHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.pip_dismiss_gradient_height); - final int bottomMargin = mContext.getResources().getDimensionPixelSize( - R.dimen.pip_dismiss_text_bottom_margin); - mTargetSlop = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_dismiss_slop); - - // Create a new view for the dismiss target - LayoutInflater inflater = LayoutInflater.from(mContext); - mDismissView = inflater.inflate(R.layout.pip_dismiss_view, null); - mDismissView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - mDismissView.forceHasOverlappingRendering(false); - - // Set the gradient background - Drawable gradient = mContext.getResources().getDrawable(R.drawable.pip_dismiss_scrim); - gradient.setAlpha((int) (255 * 0.85f)); - mDismissView.setBackground(gradient); - - // Adjust bottom margins of the text - mTargetView = mDismissView.findViewById(R.id.pip_dismiss_text); - FrameLayout.LayoutParams tlp = (FrameLayout.LayoutParams) mTargetView.getLayoutParams(); - tlp.bottomMargin = stableInsets.bottom + bottomMargin; - mTargetView.setLayoutParams(tlp); - - // Add the target to the window - LayoutParams lp = new LayoutParams( - LayoutParams.MATCH_PARENT, gradientHeight, - 0, mWindowSize.y - gradientHeight, - LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - LayoutParams.FLAG_LAYOUT_IN_SCREEN - | LayoutParams.FLAG_NOT_TOUCHABLE - | LayoutParams.FLAG_NOT_FOCUSABLE, - PixelFormat.TRANSLUCENT); - lp.setTitle("pip-dismiss-overlay"); - lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; - lp.setFitInsetsTypes(0 /* types */); - mWindowManager.addView(mDismissView, lp); - } - mDismissView.animate().cancel(); - } - - - /** - * Updates the dismiss target based on location of the view, only used for bubbles not for PIP. - * - * @return whether the view is within the dismiss target. - */ - public boolean updateTarget(View view) { - if (mDismissView == null) { - return false; - } - if (mDismissView.getAlpha() > 0) { - view.getLocationOnScreen(mLoc); - Rect viewRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + view.getWidth(), - mLoc[1] + view.getHeight()); - mTargetView.getLocationOnScreen(mLoc); - Rect targetRect = new Rect(mLoc[0], mLoc[1], mLoc[0] + mTargetView.getWidth(), - mLoc[1] + mTargetView.getHeight()); - expandRect(targetRect, mTargetSlop); - boolean intersecting = targetRect.intersect(viewRect); - if (intersecting != mIntersecting) { - // TODO: is this the right effect? - mVibe.vibrate(VibrationEffect.get(intersecting - ? VibrationEffect.EFFECT_CLICK - : VibrationEffect.EFFECT_TICK)); - } - mIntersecting = intersecting; - return intersecting; - } - return false; - } - - /** - * Shows the dismiss target. - */ - public void showDismissTarget() { - mDismissView.animate() - .alpha(1f) - .setInterpolator(Interpolators.LINEAR) - .setStartDelay(SHOW_TARGET_DELAY) - .setDuration(SHOW_TARGET_DURATION) - .start(); - } - - /** - * Hides and destroys the dismiss target. - */ - public void destroyDismissTarget() { - if (mDismissView != null) { - mDismissView.animate() - .alpha(0f) - .setInterpolator(Interpolators.LINEAR) - .setStartDelay(0) - .setDuration(HIDE_TARGET_DURATION) - .withEndAction(new Runnable() { - @Override - public void run() { - mWindowManager.removeViewImmediate(mDismissView); - mDismissView = null; - } - }) - .start(); - } - } - - private void expandRect(Rect outRect, int expandAmount) { - outRect.left = Math.max(0, outRect.left - expandAmount); - outRect.top = Math.max(0, outRect.top - expandAmount); - outRect.right = Math.min(mWindowSize.x, outRect.right + expandAmount); - outRect.bottom = Math.min(mWindowSize.y, outRect.bottom + expandAmount); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 8397c65dbdb0..a192afceddb9 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -24,8 +24,6 @@ import android.annotation.Nullable; import android.app.ActivityManager.StackInfo; import android.app.IActivityTaskManager; import android.content.Context; -import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; import android.os.Debug; import android.os.RemoteException; @@ -40,6 +38,7 @@ import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.util.FloatingContentCoordinator; import com.android.systemui.util.animation.FloatProperties; import com.android.systemui.util.animation.PhysicsAnimator; +import com.android.systemui.util.magnetictarget.MagnetizedObject; import java.io.PrintWriter; import java.util.function.Consumer; @@ -61,9 +60,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** Friction to use for PIP when it moves via physics fling animations. */ private static final float DEFAULT_FRICTION = 2f; - // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP - private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f; - private final Context mContext; private final IActivityTaskManager mActivityTaskManager; private final PipTaskOrganizer mPipTaskOrganizer; @@ -103,7 +99,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** * Update listener that resizes the PIP to {@link #mAnimatedBounds}. */ - private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener = + final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener = (target, values) -> resizePipUnchecked(mAnimatedBounds); /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */ @@ -122,6 +118,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set; + /** + * Whether we're springing to the touch event location (vs. moving it to that position + * instantly). We spring-to-touch after PIP is dragged out of the magnetic target, since it was + * 'stuck' in the target and needs to catch up to the touch location. + */ + private boolean mSpringingToTouch = false; + public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager, PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController, PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils, @@ -211,9 +214,35 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mFloatingContentCoordinator.onContentMoved(this); } - cancelAnimations(); - resizePipUnchecked(toBounds); - mBounds.set(toBounds); + if (!mSpringingToTouch) { + // If we are moving PIP directly to the touch event locations, cancel any animations and + // move PIP to the given bounds. + cancelAnimations(); + resizePipUnchecked(toBounds); + mBounds.set(toBounds); + } else { + // If PIP is 'catching up' after being stuck in the dismiss target, update the animation + // to spring towards the new touch location. + mAnimatedBoundsPhysicsAnimator + .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig) + .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig) + .withEndActions(() -> mSpringingToTouch = false); + + startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */); + } + } + + /** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */ + void setSpringingToTouch(boolean springingToTouch) { + if (springingToTouch) { + mAnimatedBounds.set(mBounds); + } + + mSpringingToTouch = springingToTouch; + } + + void prepareForAnimation() { + mAnimatedBounds.set(mBounds); } /** @@ -278,24 +307,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } /** - * @return whether the PiP at the current bounds should be dismissed. - */ - boolean shouldDismissPip() { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - final int y = displaySize.y - mStableInsets.bottom; - if (mBounds.bottom > y) { - float offscreenFraction = (float) (mBounds.bottom - y) / mBounds.height(); - return offscreenFraction >= DISMISS_OFFSCREEN_FRACTION; - } - return false; - } - - /** * Flings the PiP to the closest snap target. */ void flingToSnapTarget( - float velocityX, float velocityY, Runnable updateAction, @Nullable Runnable endAction) { + float velocityX, float velocityY, + @Nullable Runnable updateAction, @Nullable Runnable endAction) { mAnimatedBounds.set(mBounds); mAnimatedBoundsPhysicsAnimator .flingThenSpring( @@ -303,9 +319,13 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, true /* flingMustReachMinOrMax */) .flingThenSpring( FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig) - .addUpdateListener((target, values) -> updateAction.run()) .withEndActions(endAction); + if (updateAction != null) { + mAnimatedBoundsPhysicsAnimator.addUpdateListener( + (target, values) -> updateAction.run()); + } + final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right; final float estimatedFlingYEndValue = PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY); @@ -338,16 +358,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * Animates the dismissal of the PiP off the edge of the screen. */ void animateDismiss(float velocityX, float velocityY, @Nullable Runnable updateAction) { - final float velocity = PointF.length(velocityX, velocityY); - final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond(); - final Point dismissEndPoint = getDismissEndPoint(mBounds, velocityX, velocityY, isFling); - mAnimatedBounds.set(mBounds); - // Animate to the dismiss end point, and then dismiss PIP. + // Animate off the bottom of the screen, then dismiss PIP. mAnimatedBoundsPhysicsAnimator - .spring(FloatProperties.RECT_X, dismissEndPoint.x, velocityX, mSpringConfig) - .spring(FloatProperties.RECT_Y, dismissEndPoint.y, velocityY, mSpringConfig) + .spring(FloatProperties.RECT_Y, + mBounds.bottom + mBounds.height(), + velocityY, + mSpringConfig) .withEndActions(this::dismissPip); // If we were provided with an update action, run it whenever there's an update. @@ -356,7 +374,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, (target, values) -> updateAction.run()); } - startBoundsAnimator(dismissEndPoint.x /* toX */, dismissEndPoint.y /* toY */); + startBoundsAnimator(mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */); } /** @@ -408,6 +426,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private void cancelAnimations() { mAnimatedBoundsPhysicsAnimator.cancel(); mAnimatingToBounds.setEmpty(); + mSpringingToTouch = false; } /** Set new fling configs whose min/max values respect the given movement bounds. */ @@ -426,7 +445,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, * the 'real' bounds to equal the final animated bounds. */ private void startBoundsAnimator(float toX, float toY) { - cancelAnimations(); + if (!mSpringingToTouch) { + cancelAnimations(); + } // Set animatingToBounds directly to avoid allocating a new Rect, but then call // setAnimatingToBounds to run the normal logic for changing animatingToBounds. @@ -484,47 +505,29 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } /** - * @return the coordinates the PIP should animate to based on the direction of velocity when - * dismissing. + * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the + * magnetic dismiss target so it can calculate PIP's size and position. */ - private Point getDismissEndPoint(Rect pipBounds, float velX, float velY, boolean isFling) { - Point displaySize = new Point(); - mContext.getDisplay().getRealSize(displaySize); - final float bottomBound = displaySize.y + pipBounds.height() * .1f; - if (isFling && velX != 0 && velY != 0) { - // Line is defined by: y = mx + b, m = slope, b = y-intercept - // Find the slope - final float slope = velY / velX; - // Sub in slope and PiP position to solve for y-intercept: b = y - mx - final float yIntercept = pipBounds.top - slope * pipBounds.left; - // Now find the point on this line when y = bottom bound: x = (y - b) / m - final float x = (bottomBound - yIntercept) / slope; - return new Point((int) x, (int) bottomBound); - } else { - // If it wasn't a fling the velocity on 'up' is not reliable for direction of movement, - // just animate downwards. - return new Point(pipBounds.left, (int) bottomBound); - } - } + MagnetizedObject<Rect> getMagnetizedPip() { + return new MagnetizedObject<Rect>( + mContext, mAnimatedBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) { + @Override + public float getWidth(@NonNull Rect animatedPipBounds) { + return animatedPipBounds.width(); + } - /** - * @return whether the gesture it towards the dismiss area based on the velocity when - * dismissing. - */ - public boolean isGestureToDismissArea(Rect pipBounds, float velX, float velY, - boolean isFling) { - Point endpoint = getDismissEndPoint(pipBounds, velX, velY, isFling); - // Center the point - endpoint.x += pipBounds.width() / 2; - endpoint.y += pipBounds.height() / 2; - - // The dismiss area is the middle third of the screen, half the PIP's height from the bottom - Point size = new Point(); - mContext.getDisplay().getRealSize(size); - final int left = size.x / 3; - Rect dismissArea = new Rect(left, size.y - (pipBounds.height() / 2), left * 2, - size.y + pipBounds.height()); - return dismissArea.contains(endpoint.x, endpoint.y); + @Override + public float getHeight(@NonNull Rect animatedPipBounds) { + return animatedPipBounds.height(); + } + + @Override + public void getLocationOnScreen( + @NonNull Rect animatedPipBounds, @NonNull int[] loc) { + loc[0] = animatedPipBounds.left; + loc[1] = animatedPipBounds.top; + } + }; } public void dump(PrintWriter pw, String prefix) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index 7cc2759ad59a..bbb493966533 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -20,11 +20,13 @@ import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STAT import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE; +import android.annotation.SuppressLint; import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; +import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -33,14 +35,23 @@ import android.os.RemoteException; import android.util.Log; import android.util.Pair; import android.util.Size; +import android.view.Gravity; import android.view.IPinnedStackController; import android.view.InputEvent; import android.view.MotionEvent; +import android.view.View; import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.logging.MetricsLoggerWrapper; @@ -51,7 +62,10 @@ import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.DismissCircleView; import com.android.systemui.util.FloatingContentCoordinator; +import com.android.systemui.util.animation.PhysicsAnimator; +import com.android.systemui.util.magnetictarget.MagnetizedObject; import java.io.PrintWriter; @@ -62,9 +76,6 @@ import java.io.PrintWriter; public class PipTouchHandler { private static final String TAG = "PipTouchHandler"; - // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed. - private static final boolean ENABLE_FLING_DISMISS = false; - private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225; private static final int BOTTOM_OFFSET_BUFFER_DP = 1; @@ -73,17 +84,45 @@ public class PipTouchHandler { // Allow PIP to resize to a slightly bigger state upon touch private final boolean mEnableResize; private final Context mContext; + private final WindowManager mWindowManager; private final IActivityManager mActivityManager; private final PipBoundsHandler mPipBoundsHandler; private PipResizeGestureHandler mPipResizeGestureHandler; private IPinnedStackController mPinnedStackController; private final PipMenuActivityController mMenuController; - private final PipDismissViewController mDismissViewController; private final PipSnapAlgorithm mSnapAlgorithm; private final AccessibilityManager mAccessibilityManager; private boolean mShowPipMenuOnAnimationEnd = false; + /** + * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move + * PIP. + */ + private MagnetizedObject<Rect> mMagnetizedPip; + + /** + * Container for the dismiss circle, so that it can be animated within the container via + * translation rather than within the WindowManager via slow layout animations. + */ + private ViewGroup mTargetViewContainer; + + /** Circle view used to render the dismiss target. */ + private DismissCircleView mTargetView; + + /** + * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius. + */ + private MagnetizedObject.MagneticTarget mMagneticTarget; + + /** PhysicsAnimator instance for animating the dismiss target in/out. */ + private PhysicsAnimator<View> mMagneticTargetAnimator; + + /** Default configuration to use for springing the dismiss target in/out. */ + private final PhysicsAnimator.SpringConfig mTargetSpringConfig = + new PhysicsAnimator.SpringConfig( + SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_NO_BOUNCY); + // The current movement bounds private Rect mMovementBounds = new Rect(); // The current resized bounds, changed by user resize. @@ -104,21 +143,20 @@ public class PipTouchHandler { private int mDeferResizeToNormalBoundsUntilRotation = -1; private int mDisplayRotation; + /** + * Runnable that can be posted delayed to show the target. This needs to be saved as a member + * variable so we can pass it to removeCallbacks. + */ + private Runnable mShowTargetAction = this::showDismissTargetMaybe; + private Handler mHandler = new Handler(); - private Runnable mShowDismissAffordance = new Runnable() { - @Override - public void run() { - if (mEnableDismissDragToEdge) { - mDismissViewController.showDismissTarget(); - } - } - }; // Behaviour states private int mMenuState = MENU_STATE_NONE; private boolean mIsImeShowing; private int mImeHeight; private int mImeOffset; + private int mDismissAreaHeight; private boolean mIsShelfShowing; private int mShelfHeight; private int mMovementBoundsExtraOffsets; @@ -168,6 +206,7 @@ public class PipTouchHandler { } } + @SuppressLint("InflateParams") public PipTouchHandler(Context context, IActivityManager activityManager, IActivityTaskManager activityTaskManager, PipMenuActivityController menuController, InputConsumerController inputConsumerController, @@ -180,9 +219,9 @@ public class PipTouchHandler { mContext = context; mActivityManager = activityManager; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mMenuController = menuController; mMenuController.addListener(new PipMenuListener()); - mDismissViewController = new PipDismissViewController(context); mSnapAlgorithm = pipSnapAlgorithm; mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(), 2.5f); @@ -200,6 +239,7 @@ public class PipTouchHandler { mExpandedShortestEdgeSize = res.getDimensionPixelSize( R.dimen.pip_expanded_shortest_edge_size); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); + mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height); mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu); @@ -212,6 +252,56 @@ public class PipTouchHandler { mFloatingContentCoordinator = floatingContentCoordinator; mConnection = new PipAccessibilityInteractionConnection(mMotionHelper, this::onAccessibilityShowMenu, mHandler); + + final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size); + mTargetView = new DismissCircleView(context); + final FrameLayout.LayoutParams newParams = + new FrameLayout.LayoutParams(targetSize, targetSize); + newParams.gravity = Gravity.CENTER; + mTargetView.setLayoutParams(newParams); + + mTargetViewContainer = new FrameLayout(context); + mTargetViewContainer.setClipChildren(false); + mTargetViewContainer.addView(mTargetView); + + mMagnetizedPip = mMotionHelper.getMagnetizedPip(); + mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0); + mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener); + mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() { + @Override + public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) { + mMotionHelper.prepareForAnimation(); + + // Show the dismiss target, in case the initial touch event occurred within the + // magnetic field radius. + showDismissTargetMaybe(); + } + + @Override + public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target, + float velX, float velY, boolean wasFlungOut) { + if (wasFlungOut) { + mMotionHelper.flingToSnapTarget(velX, velY, null, null); + hideDismissTarget(); + } else { + mMotionHelper.setSpringingToTouch(true); + } + } + + @Override + public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { + mHandler.post(() -> { + mMotionHelper.animateDismiss(0, 0, null); + hideDismissTarget(); + }); + + + MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, + PipUtils.getTopPipActivity(mContext, mActivityManager)); + } + }); + + mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView); } public void setTouchGesture(PipTouchGesture gesture) { @@ -231,7 +321,8 @@ public class PipTouchHandler { } public void onActivityPinned() { - cleanUpDismissTarget(); + createDismissTargetMaybe(); + mShowPipMenuOnAnimationEnd = true; mPipResizeGestureHandler.onActivityPinned(); mFloatingContentCoordinator.onContentAdded(mMotionHelper); @@ -264,6 +355,10 @@ public class PipTouchHandler { public void onConfigurationChanged() { mMotionHelper.onConfigurationChanged(); mMotionHelper.synchronizePinnedStackBounds(); + + // Recreate the dismiss target for the new orientation. + cleanUpDismissTarget(); + createDismissTargetMaybe(); } public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { @@ -351,6 +446,74 @@ public class PipTouchHandler { } } + /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */ + private void createDismissTargetMaybe() { + if (!mTargetViewContainer.isAttachedToWindow()) { + mHandler.removeCallbacks(mShowTargetAction); + mMagneticTargetAnimator.cancel(); + + final Point windowSize = new Point(); + mWindowManager.getDefaultDisplay().getRealSize(windowSize); + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, + mDismissAreaHeight, + 0, windowSize.y - mDismissAreaHeight, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSLUCENT); + lp.setTitle("pip-dismiss-overlay"); + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + lp.setFitInsetsTypes(0 /* types */); + + mTargetViewContainer.setVisibility(View.INVISIBLE); + mWindowManager.addView(mTargetViewContainer, lp); + } + } + + /** Makes the dismiss target visible and animates it in, if it isn't already visible. */ + private void showDismissTargetMaybe() { + createDismissTargetMaybe(); + + if (mTargetViewContainer.getVisibility() != View.VISIBLE) { + + mTargetView.setTranslationY(mTargetViewContainer.getHeight()); + mTargetViewContainer.setVisibility(View.VISIBLE); + + // Set the magnetic field radius to half of PIP's width. + mMagneticTarget.setMagneticFieldRadiusPx(mMotionHelper.getBounds().width()); + + // Cancel in case we were in the middle of animating it out. + mMagneticTargetAnimator.cancel(); + mMagneticTargetAnimator + .spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig) + .start(); + } + } + + /** Animates the magnetic dismiss target out and then sets it to GONE. */ + private void hideDismissTarget() { + mHandler.removeCallbacks(mShowTargetAction); + mMagneticTargetAnimator + .spring(DynamicAnimation.TRANSLATION_Y, + mTargetViewContainer.getHeight(), + mTargetSpringConfig) + .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) + .start(); + } + + /** + * Removes the dismiss target and cancels any pending callbacks to show it. + */ + private void cleanUpDismissTarget() { + mHandler.removeCallbacks(mShowTargetAction); + + if (mTargetViewContainer.isAttachedToWindow()) { + mWindowManager.removeView(mTargetViewContainer); + } + } + private void onRegistrationChanged(boolean isRegistered) { mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered ? mConnection : null); @@ -375,8 +538,24 @@ public class PipTouchHandler { if (mPinnedStackController == null) { return true; } + MotionEvent ev = (MotionEvent) inputEvent; + if (mMagnetizedPip.maybeConsumeMotionEvent(ev)) { + // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event + // to the touch state. Touch state needs a DOWN event in order to later process MOVE + // events it'll receive if the object is dragged out of the magnetic field. + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mTouchState.onTouchEvent(ev); + } + + // Continue tracking velocity when the object is in the magnetic field, since we want to + // respect touch input velocity if the object is dragged out and then flung. + mTouchState.addMovementToVelocityTracker(ev); + + return true; + } + // Update the touch state mTouchState.onTouchEvent(ev); @@ -600,17 +779,13 @@ public class PipTouchHandler { mDelta.set(0f, 0f); mStartPosition.set(bounds.left, bounds.top); mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom; + mMotionHelper.setSpringingToTouch(false); // If the menu is still visible then just poke the menu // so that it will timeout after the user stops touching it if (mMenuState != MENU_STATE_NONE) { mMenuController.pokeMenu(); } - - if (mEnableDismissDragToEdge) { - mDismissViewController.createDismissTarget(); - mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY); - } } @Override @@ -623,8 +798,10 @@ public class PipTouchHandler { mSavedSnapFraction = -1f; if (mEnableDismissDragToEdge) { - mHandler.removeCallbacks(mShowDismissAffordance); - mDismissViewController.showDismissTarget(); + if (mTargetViewContainer.getVisibility() != View.VISIBLE) { + mHandler.removeCallbacks(mShowTargetAction); + showDismissTargetMaybe(); + } } } @@ -644,10 +821,6 @@ public class PipTouchHandler { mTmpBounds.offsetTo((int) left, (int) top); mMotionHelper.movePip(mTmpBounds, true /* isDragging */); - if (mEnableDismissDragToEdge) { - updateDismissFraction(); - } - final PointF curPos = touchState.getLastTouchPosition(); if (mMovementWithinDismiss) { // Track if movement remains near the bottom edge to identify swipe to dismiss @@ -661,9 +834,7 @@ public class PipTouchHandler { @Override public boolean onUp(PipTouchState touchState) { if (mEnableDismissDragToEdge) { - // Clean up the dismiss target regardless of the touch state in case the touch - // enabled state changes while the user is interacting - cleanUpDismissTarget(); + hideDismissTarget(); } if (!touchState.isUserInteracting()) { @@ -671,26 +842,8 @@ public class PipTouchHandler { } final PointF vel = touchState.getVelocity(); - final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y); final float velocity = PointF.length(vel.x, vel.y); final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond(); - final boolean isUpWithinDimiss = ENABLE_FLING_DISMISS - && touchState.getLastTouchPosition().y >= mMovementBounds.bottom - && mMotionHelper.isGestureToDismissArea(mMotionHelper.getBounds(), vel.x, - vel.y, isFling); - final boolean isFlingToBot = isFling && vel.y > 0 && !isHorizontal - && (mMovementWithinDismiss || isUpWithinDimiss); - if (mEnableDismissDragToEdge) { - // Check if the user dragged or flung the PiP offscreen to dismiss it - if (mMotionHelper.shouldDismissPip() || isFlingToBot) { - MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext, - PipUtils.getTopPipActivity(mContext, mActivityManager)); - mMotionHelper.animateDismiss( - vel.x, vel.y, - PipTouchHandler.this::updateDismissFraction /* updateAction */); - return true; - } - } if (touchState.isDragging()) { Runnable endAction = null; @@ -749,14 +902,6 @@ public class PipTouchHandler { } /** - * Removes the dismiss target and cancels any pending callbacks to show it. - */ - private void cleanUpDismissTarget() { - mHandler.removeCallbacks(mShowDismissAffordance); - mDismissViewController.destroyDismissTarget(); - } - - /** * @return whether the menu will resize as a part of showing the full menu. */ private boolean willResizeMenu() { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java index e3f65ef812fb..dc286c1c2de5 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java @@ -92,7 +92,7 @@ public class PipTouchState { // Initialize the velocity tracker initOrResetVelocityTracker(); - addMovement(ev); + addMovementToVelocityTracker(ev); mActivePointerId = ev.getPointerId(0); if (DEBUG) { @@ -120,7 +120,7 @@ public class PipTouchState { } // Update the velocity tracker - addMovement(ev); + addMovementToVelocityTracker(ev); int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == -1) { Log.e(TAG, "Invalid active pointer id on MOVE: " + mActivePointerId); @@ -151,7 +151,7 @@ public class PipTouchState { } // Update the velocity tracker - addMovement(ev); + addMovementToVelocityTracker(ev); int pointerIndex = ev.getActionIndex(); int pointerId = ev.getPointerId(pointerIndex); @@ -174,7 +174,7 @@ public class PipTouchState { } // Update the velocity tracker - addMovement(ev); + addMovementToVelocityTracker(ev); mVelocityTracker.computeCurrentVelocity(1000, mViewConfig.getScaledMaximumFlingVelocity()); mVelocity.set(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity()); @@ -318,6 +318,20 @@ public class PipTouchState { return -1; } + void addMovementToVelocityTracker(MotionEvent event) { + if (mVelocityTracker == null) { + return; + } + + // Add movement to velocity tracker using raw screen X and Y coordinates instead + // of window coordinates because the window frame may be moving at the same time. + float deltaX = event.getRawX() - event.getX(); + float deltaY = event.getRawY() - event.getY(); + event.offsetLocation(deltaX, deltaY); + mVelocityTracker.addMovement(event); + event.offsetLocation(-deltaX, -deltaY); + } + private void initOrResetVelocityTracker() { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); @@ -333,16 +347,6 @@ public class PipTouchState { } } - private void addMovement(MotionEvent event) { - // Add movement to velocity tracker using raw screen X and Y coordinates instead - // of window coordinates because the window frame may be moving at the same time. - float deltaX = event.getRawX() - event.getX(); - float deltaY = event.getRawY() - event.getY(); - event.offsetLocation(deltaX, deltaY); - mVelocityTracker.addMovement(event); - event.offsetLocation(-deltaX, -deltaY); - } - public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 8fa64d3aaf0c..e6876bd98d21 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -66,7 +66,10 @@ public class QuickQSPanel extends QSPanel { private int mMaxTiles; protected QSPanel mFullPanel; private QuickQSMediaPlayer mMediaPlayer; + /** Whether or not the QS media player feature is enabled. */ private boolean mUsingMediaPlayer; + /** Whether or not the QuickQSPanel currently contains a media player. */ + private boolean mHasMediaPlayer; private LinearLayout mHorizontalLinearLayout; // Only used with media @@ -185,8 +188,8 @@ public class QuickQSPanel extends QSPanel { boolean switchTileLayout() { if (!mUsingMediaPlayer) return false; - if (mMediaPlayer.hasMediaSession() - && mHorizontalLinearLayout.getVisibility() == View.GONE) { + mHasMediaPlayer = mMediaPlayer.hasMediaSession(); + if (mHasMediaPlayer && mHorizontalLinearLayout.getVisibility() == View.GONE) { mHorizontalLinearLayout.setVisibility(View.VISIBLE); ((View) mRegularTileLayout).setVisibility(View.GONE); mTileLayout.setListening(false); @@ -198,8 +201,7 @@ public class QuickQSPanel extends QSPanel { if (mHost != null) setTiles(mHost.getTiles()); mTileLayout.setListening(mListening); return true; - } else if (!mMediaPlayer.hasMediaSession() - && mHorizontalLinearLayout.getVisibility() == View.VISIBLE) { + } else if (!mHasMediaPlayer && mHorizontalLinearLayout.getVisibility() == View.VISIBLE) { mHorizontalLinearLayout.setVisibility(View.GONE); ((View) mRegularTileLayout).setVisibility(View.VISIBLE); mTileLayout.setListening(false); @@ -215,8 +217,9 @@ public class QuickQSPanel extends QSPanel { return false; } - public boolean hasMediaPlayerSession() { - return mMediaPlayer.hasMediaSession(); + /** Returns true if this panel currently contains a media player. */ + public boolean hasMediaPlayer() { + return mHasMediaPlayer; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 11b625f41737..e1ffad4df2c3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -344,7 +344,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements if (mQsDisabled) { lp.height = resources.getDimensionPixelSize( com.android.internal.R.dimen.quick_qs_offset_height); - } else if (useQsMediaPlayer(mContext) && mHeaderQsPanel.hasMediaPlayerSession()) { + } else if (useQsMediaPlayer(mContext) && mHeaderQsPanel.hasMediaPlayer()) { lp.height = Math.max(getMinimumHeight(), resources.getDimensionPixelSize( com.android.internal.R.dimen.quick_qs_total_height_with_media)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index fd44f04a0d80..d2da2628276a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -74,6 +74,7 @@ class NotificationShadeDepthController @Inject constructor( var shadeSpring = DepthAnimation() @VisibleForTesting var globalActionsSpring = DepthAnimation() + var showingHomeControls: Boolean = false @VisibleForTesting var brightnessMirrorSpring = DepthAnimation() @@ -133,7 +134,14 @@ class NotificationShadeDepthController @Inject constructor( shadeRadius = 0f } } - val blur = max(shadeRadius.toInt(), globalActionsSpring.radius) + + // Home controls have black background, this means that we should not have blur when they + // are fully visible, otherwise we'll enter Client Composition unnecessarily. + var globalActionsRadius = globalActionsSpring.radius + if (showingHomeControls) { + globalActionsRadius = 0 + } + val blur = max(shadeRadius.toInt(), globalActionsRadius) blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur) try { wallpaperManager.setWallpaperZoomOut(root.windowToken, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 1696f0715865..53ec57090321 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -103,12 +103,20 @@ class ConversationNotificationManager @Inject constructor( override fun onEntryInflated(entry: NotificationEntry) { if (!entry.ranking.isConversation) return fun updateCount(isExpanded: Boolean) { - if (isExpanded && !notifPanelCollapsed) { + if (isExpanded && (!notifPanelCollapsed || entry.isPinnedAndExpanded())) { resetCount(entry.key) entry.row?.let(::resetBadgeUi) } } - entry.row?.setOnExpansionChangedListener(::updateCount) + entry.row?.setOnExpansionChangedListener { isExpanded -> + if (entry.row?.isShown == true && isExpanded) { + entry.row.performOnIntrinsicHeightReached { + updateCount(isExpanded) + } + } else { + updateCount(isExpanded) + } + } updateCount(entry.row?.isExpanded == true) } @@ -169,7 +177,8 @@ class ConversationNotificationManager @Inject constructor( private fun resetBadgeUi(row: ExpandableNotificationRow): Unit = (row.layouts?.asSequence() ?: emptySequence()) - .mapNotNull { layout -> layout.contractedChild as? ConversationLayout } + .flatMap { layout -> layout.allViews.asSequence()} + .mapNotNull { view -> view as? ConversationLayout } .forEach { convoLayout -> convoLayout.setUnreadCount(0) } private data class ConversationState(val unreadCount: Int, val notification: Notification) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index dd7be2775209..c1ba26d034ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -601,6 +601,13 @@ public final class NotificationEntry extends ListEntry { return row != null && row.isPinned(); } + /** + * Is this entry pinned and was expanded while doing so + */ + public boolean isPinnedAndExpanded() { + return row != null && row.isPinnedAndExpanded(); + } + public void setRowPinned(boolean pinned) { if (row != null) row.setPinned(pinned); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 2917346153d2..5c578dfc5744 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -284,6 +284,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (isPinned()) { nowExpanded = !mExpandedWhenPinned; mExpandedWhenPinned = nowExpanded; + // Also notify any expansion changed listeners. This is necessary since the + // expansion doesn't actually change (it's already system expanded) but it + // changes visually + if (mExpansionChangedListener != null) { + mExpansionChangedListener.onExpansionChanged(nowExpanded); + } } else { nowExpanded = !isExpanded(); setUserExpanded(nowExpanded); @@ -326,6 +332,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationInlineImageResolver mImageResolver; private NotificationMediaManager mMediaManager; @Nullable private OnExpansionChangedListener mExpansionChangedListener; + @Nullable private Runnable mOnIntrinsicHeightReachedRunnable; private SystemNotificationAsyncTask mSystemNotificationAsyncTask = new SystemNotificationAsyncTask(); @@ -358,6 +365,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return Arrays.copyOf(mLayouts, mLayouts.length); } + /** + * Is this entry pinned and was expanded while doing so + */ + public boolean isPinnedAndExpanded() { + if (!isPinned()) { + return false; + } + return mExpandedWhenPinned; + } + @Override public boolean isGroupExpansionChanging() { if (isChildInGroup()) { @@ -2690,6 +2707,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mMenuRow != null && mMenuRow.getMenuView() != null) { mMenuRow.onParentHeightUpdate(); } + handleIntrinsicHeightReached(); } @Override @@ -2907,6 +2925,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mExpansionChangedListener = listener; } + /** + * Perform an action when the notification height has reached its intrinsic height. + * + * @param runnable the runnable to run + */ + public void performOnIntrinsicHeightReached(@Nullable Runnable runnable) { + mOnIntrinsicHeightReachedRunnable = runnable; + handleIntrinsicHeightReached(); + } + + private void handleIntrinsicHeightReached() { + if (mOnIntrinsicHeightReachedRunnable != null + && getActualHeight() == getIntrinsicHeight()) { + mOnIntrinsicHeightReachedRunnable.run(); + mOnIntrinsicHeightReachedRunnable = null; + } + } + @Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java index 893e8490eb90..e4e3ebcf7671 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipeline.java @@ -151,9 +151,12 @@ public final class NotifBindPipeline { * the real work once rather than repeatedly start and cancel it. */ private void requestPipelineRun(NotificationEntry entry) { - mLogger.logRequestPipelineRun(entry.getKey()); - final BindEntry bindEntry = getBindEntry(entry); + if (bindEntry.row == null) { + // Row is not managed yet but may be soon. Stop for now. + return; + } + mLogger.logRequestPipelineRun(entry.getKey()); // Abort any existing pipeline run mStage.abortStage(entry, bindEntry.row); @@ -177,10 +180,6 @@ public final class NotifBindPipeline { final BindEntry bindEntry = mBindEntries.get(entry); final ExpandableNotificationRow row = bindEntry.row; - if (row == null) { - // Row is not managed yet but may be soon. Stop for now. - return; - } mStage.executeStage(entry, row, (en) -> onPipelineComplete(en)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index b18bf01ea91f..3c3f1b21fb3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; @@ -988,6 +989,14 @@ public class NotificationContentView extends FrameLayout { } } + public @NonNull View[] getAllViews() { + return new View[] { + mContractedChild, + mHeadsUpChild, + mExpandedChild, + mSingleLineView }; + } + public NotificationViewWrapper getVisibleWrapper(int visibleType) { switch (visibleType) { case VISIBLE_TYPE_EXPANDED: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index f06cfec9480a..82e02b47974c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -203,7 +203,9 @@ public class KeyguardBouncer { Log.wtf(TAG, "onFullyShown when view was null"); } else { mKeyguardView.onResume(); - mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode()); + if (mRoot != null) { + mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode()); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index d70484e9cf41..a19d35ac4e81 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -50,19 +50,22 @@ public class LockIcon extends KeyguardAffordanceView { static final int STATE_BIOMETRICS_ERROR = 3; private float mDozeAmount; private int mIconColor; - private StateProvider mStateProvider; private int mOldState; + private int mState; private boolean mPulsing; private boolean mDozing; private boolean mKeyguardJustShown; + private boolean mPredrawRegistered; private final SparseArray<Drawable> mDrawableCache = new SparseArray<>(); private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() { @Override public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); + mPredrawRegistered = false; - int newState = mStateProvider.getState(); + int newState = mState; + mOldState = mState; Drawable icon = getIcon(newState); setImageDrawable(icon, false); @@ -80,7 +83,7 @@ public class LockIcon extends KeyguardAffordanceView { @Override public void onAnimationEnd(Drawable drawable) { if (getDrawable() == animation - && newState == mStateProvider.getState() + && newState == mState && newState == STATE_SCANNING_FACE) { animation.start(); } else { @@ -100,10 +103,6 @@ public class LockIcon extends KeyguardAffordanceView { super(context, attrs); } - void setStateProvider(StateProvider stateProvider) { - mStateProvider = stateProvider; - } - @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -135,13 +134,16 @@ public class LockIcon extends KeyguardAffordanceView { return false; } - void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) { - mOldState = oldState; + void update(int newState, boolean pulsing, boolean dozing, boolean keyguardJustShown) { + mState = newState; mPulsing = pulsing; mDozing = dozing; mKeyguardJustShown = keyguardJustShown; - getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener); + if (!mPredrawRegistered) { + mPredrawRegistered = true; + getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener); + } } void setDozeAmount(float dozeAmount) { @@ -175,7 +177,7 @@ public class LockIcon extends KeyguardAffordanceView { return mDrawableCache.get(iconRes); } - static int getIconForState(int state) { + private static int getIconForState(int state) { int iconRes; switch (state) { case STATE_LOCKED: @@ -196,7 +198,7 @@ public class LockIcon extends KeyguardAffordanceView { return iconRes; } - static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing, + private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing, boolean dozing, boolean keyguardJustShown) { // Never animate when screen is off @@ -260,9 +262,4 @@ public class LockIcon extends KeyguardAffordanceView { } return LOCK_ANIM_RES_IDS[0][lockAnimIndex]; } - - interface StateProvider { - int getState(); - } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index f7c861b84a68..a633e1979bad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -352,7 +352,6 @@ public class LockscreenLockIconController { mLockIcon.setOnClickListener(this::handleClick); mLockIcon.setOnLongClickListener(this::handleLongClick); mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate); - mLockIcon.setStateProvider(this::getState); if (mLockIcon.isAttachedToWindow()) { mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon); @@ -462,7 +461,7 @@ public class LockscreenLockIconController { shouldUpdate = false; } if (shouldUpdate && mLockIcon != null) { - mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown); + mLockIcon.update(state, mPulsing, mDozing, mKeyguardJustShown); } mLastState = state; mKeyguardJustShown = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index bf5900ff24bb..95b41a141244 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -71,7 +71,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC protected boolean mPluggedIn; protected boolean mCharging; private boolean mCharged; - private boolean mPowerSave; + protected boolean mPowerSave; private boolean mAodPowerSave; private boolean mTestmode = false; private boolean mHasReceivedBattery = false; diff --git a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt index 812a1e4bc121..f27bdbfbeda0 100644 --- a/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt +++ b/packages/SystemUI/src/com/android/systemui/util/magnetictarget/MagnetizedObject.kt @@ -160,6 +160,18 @@ abstract class MagnetizedObject<T : Any>( lateinit var magnetListener: MagnetizedObject.MagnetListener /** + * Optional update listener to provide to the PhysicsAnimator that is used to spring the object + * into the target. + */ + var physicsAnimatorUpdateListener: PhysicsAnimator.UpdateListener<T>? = null + + /** + * Optional end listener to provide to the PhysicsAnimator that is used to spring the object + * into the target. + */ + var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null + + /** * Sets whether forcefully flinging the object vertically towards a target causes it to be * attracted to the target and then released immediately, despite never being dragged within the * magnetic field. @@ -479,6 +491,14 @@ abstract class MagnetizedObject<T : Any>( .spring(yProperty, yProperty.getValue(underlyingObject) + yDiff, velY, springConfig) + if (physicsAnimatorUpdateListener != null) { + animator.addUpdateListener(physicsAnimatorUpdateListener!!) + } + + if (physicsAnimatorEndListener != null) { + animator.addEndListener(physicsAnimatorEndListener!!) + } + if (after != null) { animator.withEndActions(after) } @@ -560,13 +580,15 @@ abstract class MagnetizedObject<T : Any>( private val tempLoc = IntArray(2) fun updateLocationOnScreen() { - targetView.getLocationOnScreen(tempLoc) - - // Add half of the target size to get the center, and subtract translation since the - // target could be animating in while we're doing this calculation. - centerOnScreen.set( - tempLoc[0] + targetView.width / 2f - targetView.translationX, - tempLoc[1] + targetView.height / 2f - targetView.translationY) + targetView.post { + targetView.getLocationOnScreen(tempLoc) + + // Add half of the target size to get the center, and subtract translation since the + // target could be animating in while we're doing this calculation. + centerOnScreen.set( + tempLoc[0] + targetView.width / 2f - targetView.translationX, + tempLoc[1] + targetView.height / 2f - targetView.translationY) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index bae5bb41aa5a..c22b718fa50f 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -272,13 +272,18 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } mAnimation.cancel(); } - mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE; final float defaultY = mImeSourceControl.getSurfacePosition().y; final float x = mImeSourceControl.getSurfacePosition().x; final float hiddenY = defaultY + imeSource.getFrame().height(); final float shownY = defaultY; final float startY = show ? hiddenY : shownY; final float endY = show ? shownY : hiddenY; + if (mAnimationDirection == DIRECTION_NONE && mImeShowing && show) { + // IME is already showing, so set seek to end + seekValue = shownY; + seek = true; + } + mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE; mImeShowing = show; mAnimation = ValueAnimator.ofFloat(startY, endY); mAnimation.setDuration( diff --git a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java index 1140b9aa3abb..e5da603321cd 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java +++ b/packages/SystemUI/src/com/android/systemui/wm/SystemWindows.java @@ -258,12 +258,13 @@ public class SystemWindows { Rect outVisibleInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, - Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) { + InsetsSourceControl[] outActiveControls, Point outSurfaceSize, + SurfaceControl outBLASTSurfaceControl) { int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight, viewVisibility, flags, frameNumber, outFrame, outOverscanInsets, outContentInsets, outVisibleInsets, outStableInsets, cutout, mergedConfiguration, outSurfaceControl, outInsetsState, - outSurfaceSize, outBLASTSurfaceControl); + outActiveControls, outSurfaceSize, outBLASTSurfaceControl); if (res != 0) { return res; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index 6b7a3bfce5ad..c874915e9124 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -117,12 +117,27 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } @Test - fun updateGlobalDialogVisibility_appliesBlur() { + fun updateGlobalDialogVisibility_animatesBlur() { notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root) verify(globalActionsSpring).animateTo(eq(maxBlur / 2), safeEq(root)) } @Test + fun updateGlobalDialogVisibility_appliesBlur_withoutHomeControls() { + `when`(globalActionsSpring.radius).thenReturn(maxBlur) + notificationShadeDepthController.updateBlurCallback.doFrame(0) + verify(blurUtils).applyBlur(any(), eq(maxBlur)) + } + + @Test + fun updateGlobalDialogVisibility_appliesBlur_unlessHomeControls() { + notificationShadeDepthController.showingHomeControls = true + `when`(globalActionsSpring.radius).thenReturn(maxBlur) + notificationShadeDepthController.updateBlurCallback.doFrame(0) + verify(blurUtils).applyBlur(any(), eq(0)) + } + + @Test fun updateBlurCallback_setsBlurAndZoom() { notificationShadeDepthController.updateBlurCallback.doFrame(0) verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt index f1672b1c644d..f6b7b74d4bfc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/magnetictarget/MagnetizedObjectTest.kt @@ -106,6 +106,10 @@ class MagnetizedObjectTest : SysuiTestCase() { location[1] = targetCenterY - targetSize / 2 // y = 800 } }.`when`(targetView).getLocationOnScreen(ArgumentMatchers.any()) + doAnswer { invocation -> + (invocation.arguments[0] as Runnable).run() + true + }.`when`(targetView).post(ArgumentMatchers.any()) `when`(targetView.context).thenReturn(context) magneticTarget = MagnetizedObject.MagneticTarget(targetView, magneticFieldRadius) @@ -408,6 +412,10 @@ class MagnetizedObjectTest : SysuiTestCase() { `when`(secondTargetView.width).thenReturn(targetSize) // width = 200 `when`(secondTargetView.height).thenReturn(targetSize) // height = 200 doAnswer { invocation -> + (invocation.arguments[0] as Runnable).run() + true + }.`when`(secondTargetView).post(ArgumentMatchers.any()) + doAnswer { invocation -> (invocation.arguments[0] as IntArray).also { location -> // Return the top left of the target. location[0] = secondTargetCenterX - targetSize / 2 // x = 0 diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 061ff42de60b..58972a5346c7 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -123,6 +123,8 @@ public class Watchdog extends Thread { "android.hardware.neuralnetworks@1.0::IDevice", "android.hardware.power.stats@1.0::IPowerStats", "android.hardware.sensors@1.0::ISensors", + "android.hardware.sensors@2.0::ISensors", + "android.hardware.sensors@2.1::ISensors", "android.hardware.vr@1.0::IVr", "android.system.suspend@1.0::ISystemSuspend" ); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 1248ec01e020..f07fa501d3a8 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1830,7 +1830,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throws PackageManagerException { final List<File> addedFiles = getAddedApksLocked(); if (addedFiles.isEmpty()) { - throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + String.format("Session: %d. No packages staged in %s", sessionId, + stageDir.getAbsolutePath())); } if (ArrayUtils.size(addedFiles) > 1) { @@ -1921,7 +1923,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final List<File> addedFiles = getAddedApksLocked(); if (addedFiles.isEmpty() && removeSplitList.size() == 0) { - throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged"); + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + String.format("Session: %d. No packages staged in %s", sessionId, + stageDir.getAbsolutePath())); } // Verify that all staged packages are internally consistent diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bc3a0352484b..f693555d9761 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9684,6 +9684,20 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap, String loaderIsa) { + if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName) + && Binder.getCallingUid() != Process.SYSTEM_UID) { + Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid=" + + Binder.getCallingUid()); + // Do not record dex loads from processes pretending to be system server. + // Only the system server should be assigned the package "android", so reject calls + // that don't satisfy the constraint. + // + // notifyDexLoad is a PM API callable from the app process. So in theory, apps could + // craft calls to this API and pretend to be system server. Doing so poses no particular + // danger for dex load reporting or later dexopt, however it is a sensible check to do + // in order to verify the expectations. + return; + } int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { @@ -11310,7 +11324,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.i(TAG, "Using ABIS and native lib paths from settings : " + parsedPackage.getPackageName() + " " + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage) - + ", " + + ", " + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage)); } } @@ -17572,11 +17586,13 @@ public class PackageManagerService extends IPackageManager.Stub } if (needToVerify) { + final boolean needsVerification = needsNetworkVerificationLPr(packageName); final int verificationId = mIntentFilterVerificationToken++; for (ParsedActivity a : activities) { for (ParsedIntentInfo filter : a.getIntents()) { - if (filter.handlesWebUris(true) - && needsNetworkVerificationLPr(a.getPackageName())) { + // Run verification against hosts mentioned in any web-nav intent filter, + // even if the filter matches non-web schemes as well + if (needsVerification && filter.handlesWebUris(false)) { if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString()); mIntentFilterVerifier.addOneIntentFilterVerification( diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index e8765ad973f3..ebdf85691e58 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -17,13 +17,17 @@ package com.android.server.pm.dex; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo; +import static java.util.function.Function.identity; + import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.content.pm.PackagePartitions; import android.os.FileUtils; import android.os.RemoteException; import android.os.SystemProperties; @@ -67,13 +71,12 @@ import java.util.zip.ZipEntry; */ public class DexManager { private static final String TAG = "DexManager"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob"; private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB_LIST = "pm.dexopt.priv-apps-oob-list"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final Context mContext; // Maps package name to code locations. @@ -178,12 +181,14 @@ public class DexManager { boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY || searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT; - if (primaryOrSplit && !isUsedByOtherApps) { + if (primaryOrSplit && !isUsedByOtherApps + && !PLATFORM_PACKAGE_NAME.equals(searchResult.mOwningPackageName)) { // If the dex file is the primary apk (or a split) and not isUsedByOtherApps // do not record it. This case does not bring any new usable information // and can be safely skipped. // Note this is just an optimization that makes things easier to read in the // package-dex-use file since we don't need to pollute it with redundant info. + // However, we always record system server packages. continue; } @@ -217,6 +222,23 @@ public class DexManager { } /** + * Check if the dexPath belongs to system server. + * System server can load code from different location, so we cast a wide-net here, and + * assume that if the paths is on any of the registered system partitions then it can be loaded + * by system server. + */ + private boolean isSystemServerDexPathSupportedForOdex(String dexPath) { + ArrayList<PackagePartitions.SystemPartition> partitions = + PackagePartitions.getOrderedPartitions(identity()); + for (int i = 0; i < partitions.size(); i++) { + if (partitions.get(i).containsPath(dexPath)) { + return true; + } + } + return false; + } + + /** * Read the dex usage from disk and populate the code cache locations. * @param existingPackages a map containing information about what packages * are available to what users. Only packages in this list will be @@ -607,12 +629,6 @@ public class DexManager { */ private DexSearchResult getDexPackage( ApplicationInfo loadingAppInfo, String dexPath, int userId) { - // Ignore framework code. - // TODO(calin): is there a better way to detect it? - if (dexPath.startsWith("/system/framework/")) { - return new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND); - } - // First, check if the package which loads the dex file actually owns it. // Most of the time this will be true and we can return early. PackageCodeLocations loadingPackageCodeLocations = @@ -635,6 +651,28 @@ public class DexManager { } } + // We could not find the owning package amongst regular apps. + // If the loading package is system server, see if the dex file resides + // on any of the potentially system server owning location and if so, + // assuming system server ownership. + // + // Note: We don't have any way to detect which code paths are actually + // owned by system server. We can only assume that such paths are on + // system partitions. + if (PLATFORM_PACKAGE_NAME.equals(loadingAppInfo.packageName)) { + if (isSystemServerDexPathSupportedForOdex(dexPath)) { + // We record system server dex files as secondary dex files. + // The reason is that we only record the class loader context for secondary dex + // files and we expect that all primary apks are loaded with an empty class loader. + // System server dex files may be loaded in non-empty class loader so we need to + // keep track of their context. + return new DexSearchResult(PLATFORM_PACKAGE_NAME, DEX_SEARCH_FOUND_SECONDARY); + } else { + Slog.wtf(TAG, "System server loads dex files outside paths supported for odex: " + + dexPath); + } + } + if (DEBUG) { // TODO(calin): Consider checking for /data/data symlink. // /data/data/ symlinks /data/user/0/ and there's nothing stopping apps diff --git a/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java b/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java new file mode 100644 index 000000000000..807c82d887e3 --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/SystemServerDexLoadReporter.java @@ -0,0 +1,83 @@ +/* + * 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. + */ + +package com.android.server.pm.dex; + +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; + +import android.content.pm.IPackageManager; +import android.os.RemoteException; +import android.util.Log; +import android.util.Slog; + +import dalvik.system.BaseDexClassLoader; +import dalvik.system.VMRuntime; + +import java.util.Map; + +/** + * Reports dex file use to the package manager on behalf of system server. + */ +public class SystemServerDexLoadReporter implements BaseDexClassLoader.Reporter { + private static final String TAG = "SystemServerDexLoadReporter"; + + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private final IPackageManager mPackageManager; + + private SystemServerDexLoadReporter(IPackageManager pm) { + mPackageManager = pm; + } + + @Override + public void report(Map<String, String> classLoaderContextMap) { + if (DEBUG) { + Slog.i(TAG, "Reporting " + classLoaderContextMap); + } + if (classLoaderContextMap.isEmpty()) { + Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap"); + return; + } + + try { + mPackageManager.notifyDexLoad( + PLATFORM_PACKAGE_NAME, + classLoaderContextMap, + VMRuntime.getRuntime().vmInstructionSet()); + } catch (RemoteException ignored) { + // We're in system server, it can't happen. + } + } + + /** + * Configures system server dex file reporting. + * <p>The method will install a reporter in the BaseDexClassLoader and also + * force the reporting of any dex files already loaded by the system server. + */ + public static void configureSystemServerDexReporter(IPackageManager pm) { + Slog.i(TAG, "Configuring system server dex reporter"); + + SystemServerDexLoadReporter reporter = new SystemServerDexLoadReporter(pm); + BaseDexClassLoader.setReporter(reporter); + ClassLoader currrentClassLoader = reporter.getClass().getClassLoader(); + if (currrentClassLoader instanceof BaseDexClassLoader) { + ((BaseDexClassLoader) currrentClassLoader).reportClassLoaderChain(); + } else { + Slog.wtf(TAG, "System server class loader is not a BaseDexClassLoader. type=" + + currrentClassLoader.getClass().getName()); + } + } +} diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 161f30449a52..27288d852fb2 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -30,6 +30,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.AppOpsManagerInternal; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -47,6 +48,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManagerInternal; import android.permission.PermissionControllerManager; +import android.provider.Settings; import android.provider.Telephony; import android.telecom.TelecomManager; import android.util.ArrayMap; @@ -70,7 +72,9 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; /** @@ -180,8 +184,6 @@ public final class PermissionPolicyService extends SystemService { intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); intentFilter.addDataScheme("package"); - - /* TODO ntmyren: enable receiver when test flakes are fixed getContext().registerReceiverAsUser(new BroadcastReceiver() { final List<Integer> mUserSetupUids = new ArrayList<>(200); final Map<UserHandle, PermissionControllerManager> mPermControllerManagers = @@ -232,7 +234,6 @@ public final class PermissionPolicyService extends SystemService { manager.updateUserSensitiveForApp(uid); } }, UserHandle.ALL, intentFilter, null, null); - */ } /** diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5ce63de87ee9..c47d2151a958 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -42,7 +42,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.activityTypeToString; import static android.content.Intent.ACTION_MAIN; import static android.content.Intent.CATEGORY_HOME; @@ -109,7 +108,6 @@ import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS; import static android.view.WindowManager.TRANSIT_TASK_CLOSE; import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_UNSET; -import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; @@ -5812,18 +5810,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } @VisibleForTesting - boolean shouldAnimate(int transit) { - if (task != null && !task.shouldAnimate()) { - return false; - } - final boolean isSplitScreenPrimary = - getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; - final boolean allowSplitScreenPrimaryAnimation = transit != TRANSIT_WALLPAPER_OPEN; - - // We animate always if it's not split screen primary, and only some special cases in split - // screen primary because it causes issues with stack clipping when we run an un-minimize - // animation at the same time. - return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation; + boolean shouldAnimate() { + return task == null || task.shouldAnimate(); } /** diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index f5eba96a96d1..78d2afc64f96 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -20,7 +20,6 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; @@ -685,9 +684,7 @@ class ActivityStack extends Task { return; } - setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, - false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, - false /* creating */); + setWindowingMode(windowingMode, false /* creating */); } /** @@ -709,27 +706,22 @@ class ActivityStack extends Task { * @param preferredWindowingMode the preferred windowing mode. This may not be honored depending * on the state of things. For example, WINDOWING_MODE_UNDEFINED will resolve to the * previous non-transient mode if this stack is currently in a transient mode. - * @param animate Can be used to prevent animation. - * @param showRecents Controls whether recents is shown on the other side of a split while - * entering split mode. - * @param enteringSplitScreenMode {@code true} if entering split mode. - * @param deferEnsuringVisibility Whether visibility updates are deferred. This is set when - * many operations (which can effect visibility) are being performed in bulk. * @param creating {@code true} if this is being run during ActivityStack construction. */ - void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents, - boolean enteringSplitScreenMode, boolean deferEnsuringVisibility, boolean creating) { + void setWindowingMode(int preferredWindowingMode, boolean creating) { mWmService.inSurfaceTransaction(() -> setWindowingModeInSurfaceTransaction( - preferredWindowingMode, animate, showRecents, enteringSplitScreenMode, - deferEnsuringVisibility, creating)); + preferredWindowingMode, creating)); } - private void setWindowingModeInSurfaceTransaction(int preferredWindowingMode, boolean animate, - boolean showRecents, boolean enteringSplitScreenMode, boolean deferEnsuringVisibility, + private void setWindowingModeInSurfaceTransaction(int preferredWindowingMode, boolean creating) { + final TaskDisplayArea taskDisplayArea = getDisplayArea(); + if (taskDisplayArea == null) { + Slog.d(TAG, "taskDisplayArea is null, bail early"); + return; + } final int currentMode = getWindowingMode(); final int currentOverrideMode = getRequestedOverrideWindowingMode(); - final TaskDisplayArea taskDisplayArea = getDisplayArea(); final Task topTask = getTopMostTask(); int windowingMode = preferredWindowingMode; if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED @@ -753,12 +745,9 @@ class ActivityStack extends Task { final boolean alreadyInSplitScreenMode = taskDisplayArea.isSplitScreenModeActivated(); - // Don't send non-resizeable notifications if the windowing mode changed was a side effect - // of us entering split-screen mode. - final boolean sendNonResizeableNotification = !enteringSplitScreenMode; // Take any required action due to us not supporting the preferred windowing mode. if (alreadyInSplitScreenMode && windowingMode == WINDOWING_MODE_FULLSCREEN - && sendNonResizeableNotification && isActivityTypeStandardOrUndefined()) { + && isActivityTypeStandardOrUndefined()) { final boolean preferredSplitScreen = preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; @@ -794,7 +783,7 @@ class ActivityStack extends Task { if (currentMode == WINDOWING_MODE_PINNED) { mAtmService.getTaskChangeNotificationController().notifyActivityUnpinned(); } - if (sendNonResizeableNotification && likelyResolvedMode != WINDOWING_MODE_FULLSCREEN + if (likelyResolvedMode != WINDOWING_MODE_FULLSCREEN && topActivity != null && !topActivity.noDisplay && topActivity.isNonResizableOrForcedResizable(likelyResolvedMode)) { // Inform the user that they are starting an app that may not work correctly in @@ -806,7 +795,7 @@ class ActivityStack extends Task { mAtmService.deferWindowLayout(); try { - if (!animate && topActivity != null) { + if (topActivity != null) { mStackSupervisor.mNoAnimActivities.add(topActivity); } super.setWindowingMode(windowingMode); @@ -845,36 +834,11 @@ class ActivityStack extends Task { false /*preserveWindows*/, true /*deferResume*/); } } finally { - if (showRecents && !alreadyInSplitScreenMode && isOnHomeDisplay() - && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - // Make sure recents stack exist when creating a dock stack as it normally needs to - // be on the other side of the docked stack and we make visibility decisions based - // on that. - // TODO: This is only here to help out with the case where recents stack doesn't - // exist yet. For that case the initial size of the split-screen stack will be the - // the one where the home stack is visible since recents isn't visible yet, but the - // divider will be off. I think we should just make the initial bounds that of home - // so that the divider matches and remove this logic. - // TODO: This is currently only called when entering split-screen while in another - // task, and from the tests - // TODO (b/78247419): Fix the rotation animation from fullscreen to minimized mode - final boolean isRecentsComponentHome = - mAtmService.getRecentTasks().isRecentsComponentHomeActivity(mCurrentUser); - final ActivityStack recentStack = taskDisplayArea.getOrCreateStack( - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, - isRecentsComponentHome ? ACTIVITY_TYPE_HOME : ACTIVITY_TYPE_RECENTS, - true /* onTop */); - recentStack.moveToFront("setWindowingMode"); - // If task moved to docked stack - show recents if needed. - mWmService.showRecentApps(); - } mAtmService.continueWindowLayout(); } - if (!deferEnsuringVisibility) { - mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); - mRootWindowContainer.resumeFocusedStacksTopActivities(); - } + mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + mRootWindowContainer.resumeFocusedStacksTopActivities(); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 02601ff4b6e3..1e5a924e1d4d 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -80,8 +80,6 @@ import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE; import static com.android.server.wm.Task.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; import static com.android.server.wm.Task.LOCK_TASK_AUTH_WHITELISTED; import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT; -import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE; -import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -140,7 +138,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; import com.android.internal.os.TransferPipe; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; @@ -382,71 +379,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { private boolean mInitialized; - private final MoveTaskToFullscreenHelper mMoveTaskToFullscreenHelper = - new MoveTaskToFullscreenHelper(); - private class MoveTaskToFullscreenHelper { - private TaskDisplayArea mToDisplayArea; - private boolean mOnTop; - private Task mTopTask; - private boolean mSchedulePictureInPictureModeChange; - - void process(ActivityStack fromStack, TaskDisplayArea toDisplayArea, boolean onTop, - boolean schedulePictureInPictureModeChange) { - mSchedulePictureInPictureModeChange = schedulePictureInPictureModeChange; - mToDisplayArea = toDisplayArea; - mOnTop = onTop; - mTopTask = fromStack.getTopMostTask(); - - final PooledConsumer c = PooledLambda.obtainConsumer( - MoveTaskToFullscreenHelper::processLeafTask, this, PooledLambda.__(Task.class)); - fromStack.forAllLeafTasks(c, false /* traverseTopToBottom */); - c.recycle(); - mToDisplayArea = null; - mTopTask = null; - } - - private void processLeafTask(Task task) { - // This is a one level task that we don't need to create stack for reparenting to. - if (task.isRootTask() && DisplayContent.alwaysCreateStack(WINDOWING_MODE_FULLSCREEN, - task.getActivityType())) { - final ActivityStack stack = (ActivityStack) task; - stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - if (mToDisplayArea.getDisplayId() != stack.getDisplayId()) { - stack.reparent(mToDisplayArea, mOnTop); - } else if (mOnTop) { - mToDisplayArea.positionStackAtTop(stack, false /* includingParents */); - } else { - mToDisplayArea.positionStackAtBottom(stack); - } - return; - } - - final ActivityStack toStack = mToDisplayArea.getOrCreateStack(null, mTmpOptions, task, - task.getActivityType(), mOnTop); - if (task == toStack) { - // The task was reused as the root task. - return; - } - - if (mOnTop) { - final boolean isTopTask = task == mTopTask; - // Defer resume until all the tasks have been moved to the fullscreen stack - task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, isTopTask /*animate*/, - DEFER_RESUME, mSchedulePictureInPictureModeChange, - "moveTasksToFullscreenStack - onTop"); - MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext, - task.effectiveUid, task.realActivity.flattenToString()); - } else { - // Position the tasks in the fullscreen stack in order at the bottom of the - // stack. Also defer resume until all the tasks have been moved to the - // fullscreen stack. - task.reparent(toStack, ON_TOP, REPARENT_LEAVE_STACK_IN_PLACE, - !ANIMATE, DEFER_RESUME, mSchedulePictureInPictureModeChange, - "moveTasksToFullscreenStack - NOT_onTop"); - } - } - } - /** * Description of a request to start a new activity, which has been held * due to app switches being disabled. @@ -1501,41 +1433,43 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mResizingTasksDuringAnimation.clear(); } - /** - * TODO: This should just change the windowing mode and resize vs. actually moving task around. - * Can do that once we are no longer using static stack ids. - */ - private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack, - int toDisplayId, boolean onTop) { + void setSplitScreenResizing(boolean resizing) { + if (resizing == mDockedStackResizing) { + return; + } - mService.deferWindowLayout(); - try { - final int windowingMode = fromStack.getWindowingMode(); - final TaskDisplayArea toDisplayArea = mRootWindowContainer - .getDisplayContent(toDisplayId).getDefaultTaskDisplayArea(); + mDockedStackResizing = resizing; + mWindowManager.setDockedStackResizing(resizing); + } - if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - // We are moving all tasks from the docked stack to the fullscreen stack, - // which is dismissing the docked stack, so resize all other stacks to - // fullscreen here already so we don't end up with resize trashing. - for (int i = toDisplayArea.getStackCount() - 1; i >= 0; --i) { - final ActivityStack otherStack = toDisplayArea.getStackAt(i); - if (!otherStack.inSplitScreenSecondaryWindowingMode()) { - continue; - } - otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED); - } - } + private void removePinnedStackInSurfaceTransaction(ActivityStack stack) { + /** + * Workaround: Force-stop all the activities in the pinned stack before we reparent them + * to the fullscreen stack. This is to guarantee that when we are removing a stack, + * that the client receives onStop() before it is reparented. We do this by detaching + * the stack from the display so that it will be considered invisible when + * ensureActivitiesVisible() is called, and all of its activities will be marked + * invisible as well and added to the stopping list. After which we process the + * stopping list by handling the idle. + */ + stack.cancelAnimation(); + stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); + stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */); + activityIdleInternal(null /* idleActivity */, false /* fromTimeout */, + true /* processPausingActivities */, null /* configuration */); - // If we are moving from the pinned stack, then the animation takes care of updating - // the picture-in-picture mode. - final boolean schedulePictureInPictureModeChange = - windowingMode == WINDOWING_MODE_PINNED; + // Reparent all the tasks to the bottom of the display + final DisplayContent toDisplay = + mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY); - if (fromStack.hasChild()) { - mTmpOptions.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); - mMoveTaskToFullscreenHelper.process( - fromStack, toDisplayArea, onTop, schedulePictureInPictureModeChange); + mService.deferWindowLayout(); + try { + stack.setWindowingMode(WINDOWING_MODE_UNDEFINED); + if (toDisplay.getDisplayId() != stack.getDisplayId()) { + stack.reparent(toDisplay.getDefaultTaskDisplayArea(), false /* onTop */); + } else { + toDisplay.mTaskContainers.positionStackAtBottom(stack); } mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); @@ -1545,41 +1479,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } } - void moveTasksToFullscreenStackLocked(ActivityStack fromStack, boolean onTop) { - // TODO(b/153089193): Support moving within the same task display area - mWindowManager.inSurfaceTransaction(() -> - moveTasksToFullscreenStackInSurfaceTransaction(fromStack, DEFAULT_DISPLAY, onTop)); - } - - void setSplitScreenResizing(boolean resizing) { - if (resizing == mDockedStackResizing) { - return; - } - - mDockedStackResizing = resizing; - mWindowManager.setDockedStackResizing(resizing); - } - private void removeStackInSurfaceTransaction(ActivityStack stack) { if (stack.getWindowingMode() == WINDOWING_MODE_PINNED) { - /** - * Workaround: Force-stop all the activities in the pinned stack before we reparent them - * to the fullscreen stack. This is to guarantee that when we are removing a stack, - * that the client receives onStop() before it is reparented. We do this by detaching - * the stack from the display so that it will be considered invisible when - * ensureActivitiesVisible() is called, and all of its activities will be marked - * invisible as well and added to the stopping list. After which we process the - * stopping list by handling the idle. - */ - stack.cancelAnimation(); - stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); - stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); - stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */); - activityIdleInternal(null /* idleActivity */, false /* fromTimeout */, - true /* processPausingActivities */, null /* configuration */); - - // Move all the tasks to the bottom of the fullscreen stack - moveTasksToFullscreenStackLocked(stack, !ON_TOP); + removePinnedStackInSurfaceTransaction(stack); } else { final PooledConsumer c = PooledLambda.obtainConsumer( ActivityStackSupervisor::processRemoveTask, this, PooledLambda.__(Task.class)); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 0b1968765300..c253cd2c3297 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3324,33 +3324,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found"); return; } - // Place the task in the right stack if it isn't there already based on - // the requested bounds. - // The stack transition logic is: - // - a null bounds on a freeform task moves that task to fullscreen - // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves - // that task to freeform - // - otherwise the task is not moved - ActivityStack stack = task.getStack(); if (!task.getWindowConfiguration().canResizeTask()) { throw new IllegalArgumentException("resizeTask not allowed on task=" + task); } - if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) { - stack = stack.getDisplayArea().getOrCreateStack( - WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP); - } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) { - stack = stack.getDisplayArea().getOrCreateStack( - WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP); - } // Reparent the task to the right stack if necessary boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0; - if (stack != task.getStack()) { - // Defer resume until the task is resized below - task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, - DEFER_RESUME, "resizeTask"); - preserveWindow = false; - } // After reparenting (which only resizes the task to the stack bounds), resize the // task to the actual bounds provided @@ -4022,28 +4001,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - // TODO: API should just be about changing windowing modes... - public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) { - enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, - "moveTasksToFullscreenStack()"); - synchronized (mGlobalLock) { - final long origId = Binder.clearCallingIdentity(); - try { - final ActivityStack stack = mRootWindowContainer.getStack(fromStackId); - if (stack != null){ - if (!stack.isActivityTypeStandardOrUndefined()) { - throw new IllegalArgumentException( - "You can't move tasks from non-standard stacks."); - } - mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop); - } - } finally { - Binder.restoreCallingIdentity(origId); - } - } - } - /** * Moves the top activity in the input stackId to the pinned stack. * diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index f86aeb2244dc..78ee1de78079 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -76,7 +76,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; -import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; import android.annotation.DrawableRes; @@ -1807,15 +1806,11 @@ public class AppTransition implements Dump { } int getAppStackClipMode() { - // When dismiss keyguard animation occurs, clip before the animation to prevent docked - // app from showing beyond the divider - if (mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY - || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) { - return STACK_CLIP_BEFORE_ANIM; - } return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL + || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY + || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER ? STACK_CLIP_NONE : STACK_CLIP_AFTER_ANIM; } diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 90fdf19d9781..edd14b7bebf3 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -253,6 +253,12 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { super.prepareSurfaces(); getBounds(mTmpDimBoundsRect); + // If SystemUI is dragging for recents, we want to reset the dim state so any dim layer + // on the display level fades out. + if (forAllTasks(task -> !task.canAffectSystemUiFlags())) { + mDimmer.resetDimStates(); + } + if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) { scheduleAnimation(); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a93b962c33b4..8111c0ef28c2 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -197,7 +197,6 @@ import android.view.ViewRootImpl; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerPolicyConstants.PointerEventListener; -import android.window.ITaskOrganizer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -3554,6 +3553,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo onWallpaper, goingToShade, subtle)); } }, true /* traverseTopToBottom */); + for (int i = mShellRoots.size() - 1; i >= 0; --i) { + mShellRoots.valueAt(i).startAnimation(policy.createHiddenByKeyguardExit( + onWallpaper, goingToShade, subtle)); + } } /** @return {@code true} if there is window to wait before enabling the screen. */ diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index f088dbcb0174..e9d3d56ee283 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -696,6 +696,10 @@ public class DisplayPolicy { mForceShowSystemBarsFromExternal = forceShowSystemBars; } + boolean getForceShowSystemBars() { + return mForceShowSystemBarsFromExternal; + } + public boolean hasNavigationBar() { return mHasNavigationBar; } @@ -1494,9 +1498,7 @@ public class DisplayPolicy { final int behavior = mLastBehavior; boolean navVisible = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL ? (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 - : mNavigationBar != null && mNavigationBar.getControllableInsetProvider() != null - && mNavigationBar.getControllableInsetProvider().isClientVisible() - && !mDisplayContent.getInsetsPolicy().isTransient(ITYPE_NAVIGATION_BAR); + : isNavigationBarRequestedVisible(); boolean navTranslucent = (sysui & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0; boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0 @@ -1533,6 +1535,14 @@ public class DisplayPolicy { mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation; } + boolean isNavigationBarRequestedVisible() { + final InsetsSourceProvider provider = + mDisplayContent.getInsetsStateController().peekSourceProvider(ITYPE_NAVIGATION_BAR); + return provider == null + ? InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR) + : provider.isClientVisible(); + } + void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) { // When the navigation bar isn't visible, we put up a fake input window to catch all // touch events. This way we can detect when the user presses anywhere to bring back the diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index e4e57168efe7..61a199a816df 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -99,7 +99,7 @@ class InsetsPolicy { } private void updateHideNavInputEventReceiver() { - mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR), + mPolicy.updateHideNavInputEventReceiver(mPolicy.isNavigationBarRequestedVisible(), mFocusedWin != null && mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH); } @@ -304,7 +304,10 @@ class InsetsPolicy { // We need to force system bars when the docked stack is visible, when the freeform stack // is visible but also when we are resizing for the transitions when docked stack // visibility changes. - return isDockedStackVisible || isFreeformStackVisible || isResizing; + return isDockedStackVisible + || isFreeformStackVisible + || isResizing + || mPolicy.getForceShowSystemBars(); } @VisibleForTesting diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 5f3732a58824..7d8a56e6c468 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -51,6 +51,7 @@ import android.view.IWindowId; import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InputChannel; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -158,10 +159,10 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, - InsetsState outInsetsState) { + InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel, - outInsetsState); + outInsetsState, outActiveControls); } @Override @@ -171,7 +172,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, new Rect() /* outFrame */, outContentInsets, outStableInsets, new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */, - outInsetsState); + outInsetsState, null); } @Override @@ -191,7 +192,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { Rect outStableInsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, - Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) { + InsetsSourceControl[] outActiveControls, Point outSurfaceSize, + SurfaceControl outBLASTSurfaceControl) { if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag); @@ -199,8 +201,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { requestedWidth, requestedHeight, viewFlags, flags, frameNumber, outFrame, outContentInsets, outVisibleInsets, outStableInsets, outBackdropFrame, cutout, - mergedConfiguration, outSurfaceControl, outInsetsState, outSurfaceSize, - outBLASTSurfaceControl); + mergedConfiguration, outSurfaceControl, outInsetsState, outActiveControls, + outSurfaceSize, outBLASTSurfaceControl); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index 9732637fdd4d..701feff8c6be 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -16,12 +16,20 @@ package com.android.server.wm; +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; + +import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; +import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; + import android.annotation.NonNull; +import android.graphics.Point; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import android.view.DisplayInfo; import android.view.IWindow; import android.view.SurfaceControl; +import android.view.animation.Animation; /** * Represents a piece of the hierarchy under which a client Shell can manage sub-windows. @@ -70,5 +78,29 @@ public class ShellRoot { IWindow getClient() { return mClient; } + + void startAnimation(Animation anim) { + // Only do this for the divider + if (mToken.windowType != TYPE_DOCK_DIVIDER) { + return; + } + + DisplayInfo displayInfo = mToken.getFixedRotationTransformDisplayInfo(); + if (displayInfo == null) { + displayInfo = mDisplayContent.getDisplayInfo(); + } + + // Mostly copied from WindowState to enable keyguard transition animation + anim.initialize(displayInfo.logicalWidth, displayInfo.logicalHeight, + displayInfo.appWidth, displayInfo.appHeight); + anim.restrictDuration(MAX_ANIMATION_DURATION); + anim.scaleCurrentDuration(mDisplayContent.mWmService.getWindowAnimationScaleLocked()); + final AnimationAdapter adapter = new LocalAnimationAdapter( + new WindowAnimationSpec(anim, new Point(0, 0), false /* canSkipFirstFrame */, + 0 /* windowCornerRadius */), + mDisplayContent.mWmService.mSurfaceAnimationRunner); + mToken.startAnimation(mToken.getPendingTransaction(), adapter, false /* hidden */, + ANIMATION_TYPE_WINDOW_ANIMATION, null /* animationFinishedCallback */); + } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index cb897db9a2d0..9adacb8c578d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3348,6 +3348,21 @@ class Task extends WindowContainer<WindowContainer> { @Override Dimmer getDimmer() { + // If the window is in multi-window mode, we want to dim at the Task level to ensure the dim + // bounds match the area the app lives in + if (inMultiWindowMode()) { + return mDimmer; + } + + // If we're not at the root task level, we want to keep traversing through the parents to + // find the root. + // Once at the root task level, we want to check {@link #isTranslucent(ActivityRecord)}. + // If true, we want to get the Dimmer from the level above since we don't want to animate + // the dim with the Task. + if (!isRootTask() || isTranslucent(null)) { + return super.getDimmer(); + } + return mDimmer; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 1bc7244996ab..ec3c99bf0808 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -936,9 +936,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { } } else { addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM); - stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */, - false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */, - true /* creating */); + stack.setWindowingMode(windowingMode, true /* creating */); } return stack; } @@ -1148,7 +1146,7 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { for (int i = getStackCount() - 1; i >= 0; --i) { final ActivityStack stack = getStackAt(i); // Collect the root tasks that are currently being organized. - if (stack.isOrganized()) { + if (stack.mCreatedByOrganizer) { for (int k = stack.getChildCount() - 1; k >= 0; --k) { final ActivityStack childStack = (ActivityStack) stack.getChildAt(k); if (childStack.getActivityType() == activityType) { @@ -1614,6 +1612,11 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { } } + @Override + boolean canCreateRemoteAnimationTarget() { + return true; + } + /** * Callback for when the order of the stacks in the display changes. */ diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index eb005e0f7eda..14e5c6cbf28d 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -68,6 +68,7 @@ import android.util.MergedConfiguration; import android.util.Slog; import android.view.DisplayCutout; import android.view.IWindowSession; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.Surface; import android.view.SurfaceControl; @@ -164,6 +165,7 @@ class TaskSnapshotSurface implements StartingSurface { final Rect tmpContentInsets = new Rect(); final Rect tmpStableInsets = new Rect(); final InsetsState mTmpInsetsState = new InsetsState(); + final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0]; final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); final TaskDescription taskDescription = new TaskDescription(); taskDescription.setBackgroundColor(WHITE); @@ -231,8 +233,8 @@ class TaskSnapshotSurface implements StartingSurface { } try { final int res = session.addToDisplay(window, window.mSeq, layoutParams, - View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect, - tmpCutout, null, mTmpInsetsState); + View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, + tmpRect, tmpCutout, null, mTmpInsetsState, mTempControls); if (res < 0) { Slog.w(TAG, "Failed to add snapshot starting window res=" + res); return null; @@ -249,7 +251,7 @@ class TaskSnapshotSurface implements StartingSurface { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1, tmpFrame, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState, - sTmpSurfaceSize, sTmpSurfaceControl); + mTempControls, sTmpSurfaceSize, sTmpSurfaceControl); } catch (RemoteException e) { // Local call. } @@ -377,6 +379,7 @@ class TaskSnapshotSurface implements StartingSurface { frame = null; mTmpDstFrame.set(mFrame); } + mTmpDstFrame.offsetTo(0, 0); // Scale the mismatch dimensions to fill the task bounds mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 569b8f61c4f4..f9955fa89bff 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2295,6 +2295,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) { mLastLayer = -1; reassignLayer(t); + + // Leash is now responsible for position, so set our position to 0. + t.setPosition(mSurfaceControl, 0, 0); + mLastSurfacePosition.set(0, 0); } @Override @@ -2302,6 +2306,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< mLastLayer = -1; mSurfaceFreezer.unfreeze(t); reassignLayer(t); + updateSurfacePosition(t); } /** @@ -2365,11 +2370,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } - void updateSurfacePosition() { + final void updateSurfacePosition() { + updateSurfacePosition(getPendingTransaction()); + } + + void updateSurfacePosition(Transaction t) { // Avoid fighting with the organizer over Surface position. if (isOrganized()) return; - if (mSurfaceControl == null) { + if (mSurfaceControl == null || mSurfaceAnimator.hasLeash()) { return; } @@ -2378,7 +2387,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return; } - getPendingTransaction().setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y); + t.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y); mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a1e0eb730ff7..8d3a4f27a257 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -227,6 +227,7 @@ import android.view.InputChannel; import android.view.InputDevice; import android.view.InputEvent; import android.view.InputWindowHandle; +import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.KeyEvent; import android.view.MagnificationSpec; @@ -1356,7 +1357,7 @@ public class WindowManagerService extends IWindowManager.Stub LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, - InsetsState outInsetsState) { + InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { int[] appOp = new int[1]; final boolean isRoundedCornerOverlay = (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0; @@ -1643,8 +1644,7 @@ public class WindowManagerService extends IWindowManager.Stub outStableInsets, outDisplayCutout)) { res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS; } - outInsetsState.set(win.getInsetsState(), - win.mClient instanceof IWindow.Stub /* copySource */); + outInsetsState.set(win.getInsetsState(), win.isClientLocal()); if (mInTouchMode) { res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE; @@ -1678,6 +1678,8 @@ public class WindowManagerService extends IWindowManager.Stub } displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); + getInsetsSourceControls(win, outActiveControls); + ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s" + ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5)); @@ -2080,7 +2082,8 @@ public class WindowManagerService extends IWindowManager.Stub Rect outVisibleInsets, Rect outStableInsets, Rect outBackdropFrame, DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, - Point outSurfaceSize, SurfaceControl outBLASTSurfaceControl) { + InsetsSourceControl[] outActiveControls, Point outSurfaceSize, + SurfaceControl outBLASTSurfaceControl) { int result = 0; boolean configChanged; final int pid = Binder.getCallingPid(); @@ -2375,8 +2378,8 @@ public class WindowManagerService extends IWindowManager.Stub outStableInsets); outCutout.set(win.getWmDisplayCutout().getDisplayCutout()); outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw())); - outInsetsState.set(win.getInsetsState(), - win.mClient instanceof IWindow.Stub /* copySource */); + outInsetsState.set(win.getInsetsState(), win.isClientLocal()); + getInsetsSourceControls(win, outActiveControls); if (DEBUG) { Slog.v(TAG_WM, "Relayout given client " + client.asBinder() + ", requestedWidth=" + requestedWidth @@ -2413,6 +2416,21 @@ public class WindowManagerService extends IWindowManager.Stub return result; } + private void getInsetsSourceControls(WindowState win, InsetsSourceControl[] outControls) { + if (outControls != null) { + final InsetsSourceControl[] controls = + win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win); + Arrays.fill(outControls, null); + if (controls != null) { + final int length = Math.min(controls.length, outControls.length); + for (int i = 0; i < length; i++) { + outControls[i] = win.isClientLocal() + ? new InsetsSourceControl(controls[i]) : controls[i]; + } + } + } + } + private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator, boolean focusMayChange) { // Try starting an animation; if there isn't one, we diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3ebc0f410fd7..5a76bac67d64 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3479,6 +3479,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + boolean isClientLocal() { + return mClient instanceof IWindow.Stub; + } + void updateLocationInParentDisplayIfNeeded() { final int embeddedDisplayContentsSize = mEmbeddedDisplayContents.size(); // If there is any embedded display which is re-parented to this window, we need to @@ -5233,23 +5237,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) { super.onAnimationLeashCreated(t, leash); - - // Leash is now responsible for position, so set our position to 0. - t.setPosition(mSurfaceControl, 0, 0); - mLastSurfacePosition.set(0, 0); } @Override public void onAnimationLeashLost(Transaction t) { super.onAnimationLeashLost(t); - updateSurfacePosition(t); } @Override - void updateSurfacePosition() { - updateSurfacePosition(getPendingTransaction()); - } - @VisibleForTesting void updateSurfacePosition(Transaction t) { if (mSurfaceControl == null) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 7457a1d05335..23091a00a80b 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -535,8 +535,8 @@ class WindowToken extends WindowContainer<WindowState> { } @Override - void updateSurfacePosition() { - super.updateSurfacePosition(); + void updateSurfacePosition(SurfaceControl.Transaction t) { + super.updateSurfacePosition(t); if (isFixedRotationTransforming()) { // The window is layouted in a simulated rotated display but the real display hasn't // rotated, so here transforms its surface to fit in the real display. diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index d36eae89c1ff..de24bcf02a7e 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -73,7 +73,7 @@ struct Constants { }; static const Constants& constants() { - static Constants c; + static constexpr Constants c; return c; } @@ -159,6 +159,9 @@ std::string makeBindMdName() { } } // namespace +const bool IncrementalService::sEnablePerfLogging = + android::base::GetBoolProperty("incremental.perflogging", false); + IncrementalService::IncFsMount::~IncFsMount() { incrementalService.mDataLoaderManager->destroyDataLoader(mountId); LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\''; @@ -719,7 +722,10 @@ int IncrementalService::bind(StorageId storage, std::string_view source, std::st if (storageInfo == ifs->storages.end()) { return -EINVAL; } - std::string normSource = normalizePathToStorage(ifs, storage, source); + std::string normSource = normalizePathToStorageLocked(storageInfo, source); + if (normSource.empty()) { + return -EINVAL; + } l.unlock(); std::unique_lock l2(mLock, std::defer_lock); return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource), @@ -768,22 +774,28 @@ int IncrementalService::unbind(StorageId storage, std::string_view target) { return 0; } -std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr ifs, - StorageId storage, std::string_view path) { - const auto storageInfo = ifs->storages.find(storage); - if (storageInfo == ifs->storages.end()) { - return {}; - } +std::string IncrementalService::normalizePathToStorageLocked( + IncFsMount::StorageMap::iterator storageIt, std::string_view path) { std::string normPath; if (path::isAbsolute(path)) { normPath = path::normalize(path); + if (!path::startsWith(normPath, storageIt->second.name)) { + return {}; + } } else { - normPath = path::normalize(path::join(storageInfo->second.name, path)); + normPath = path::normalize(path::join(storageIt->second.name, path)); } - if (!path::startsWith(normPath, storageInfo->second.name)) { + return normPath; +} + +std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr& ifs, + StorageId storage, std::string_view path) { + std::unique_lock l(ifs->lock); + const auto storageInfo = ifs->storages.find(storage); + if (storageInfo == ifs->storages.end()) { return {}; } - return normPath; + return normalizePathToStorageLocked(storageInfo, path); } int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id, @@ -791,7 +803,8 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m if (auto ifs = getIfs(storage)) { std::string normPath = normalizePathToStorage(ifs, storage, path); if (normPath.empty()) { - LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path; + LOG(ERROR) << "Internal error: storageId " << storage + << " failed to normalize: " << path; return -EINVAL; } auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); @@ -799,10 +812,6 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err; return err; } - std::vector<uint8_t> metadataBytes; - if (params.metadata.data && params.metadata.size > 0) { - metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size); - } return 0; } return -EINVAL; @@ -842,8 +851,9 @@ int IncrementalService::makeDirs(StorageId storageId, std::string_view path, int int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath, StorageId destStorageId, std::string_view newPath) { - if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId); - ifsSrc && ifsSrc == ifsDest) { + auto ifsSrc = getIfs(sourceStorageId); + auto ifsDest = sourceStorageId == destStorageId ? ifsSrc : getIfs(destStorageId); + if (ifsSrc && ifsSrc == ifsDest) { std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath); std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath); if (normOldPath.empty() || normNewPath.empty()) { @@ -1156,11 +1166,25 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs, return true; } -// Extract lib filse from zip, create new files in incfs and write data to them +template <class Duration> +static long elapsedMcs(Duration start, Duration end) { + return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count(); +} + +// Extract lib files from zip, create new files in incfs and write data to them bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath, std::string_view libDirRelativePath, std::string_view abi) { + namespace sc = std::chrono; + using Clock = sc::steady_clock; + auto start = Clock::now(); + const auto ifs = getIfs(storage); + if (!ifs) { + LOG(ERROR) << "Invalid storage " << storage; + return false; + } + // First prepare target directories if they don't exist yet if (auto res = makeDirs(storage, libDirRelativePath, 0755)) { LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath @@ -1168,112 +1192,145 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ return false; } - std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(apkFullPath.data())); + auto mkDirsTs = Clock::now(); + + std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(path::c_str(apkFullPath))); if (!zipFile) { LOG(ERROR) << "Failed to open zip file at " << apkFullPath; return false; } void* cookie = nullptr; const auto libFilePrefix = path::join(constants().libDir, abi); - if (!zipFile.get()->startIteration(&cookie, libFilePrefix.c_str() /* prefix */, - constants().libSuffix.data() /* suffix */)) { + if (!zipFile->startIteration(&cookie, libFilePrefix.c_str() /* prefix */, + constants().libSuffix.data() /* suffix */)) { LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath; return false; } + auto endIteration = [&zipFile](void* cookie) { zipFile->endIteration(cookie); }; + auto iterationCleaner = std::unique_ptr<void, decltype(endIteration)>(cookie, endIteration); + + auto openZipTs = Clock::now(); + + std::vector<IncFsDataBlock> instructions; ZipEntryRO entry = nullptr; - bool success = true; - while ((entry = zipFile.get()->nextEntry(cookie)) != nullptr) { + while ((entry = zipFile->nextEntry(cookie)) != nullptr) { + auto startFileTs = Clock::now(); + char fileName[PATH_MAX]; - if (zipFile.get()->getEntryFileName(entry, fileName, sizeof(fileName))) { + if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) { continue; } const auto libName = path::basename(fileName); const auto targetLibPath = path::join(libDirRelativePath, libName); const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath); // If the extract file already exists, skip - struct stat st; - if (stat(targetLibPathAbsolute.c_str(), &st) == 0) { - LOG(INFO) << "Native lib file already exists: " << targetLibPath - << "; skipping extraction"; + if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) { + if (sEnablePerfLogging) { + LOG(INFO) << "incfs: Native lib file already exists: " << targetLibPath + << "; skipping extraction, spent " + << elapsedMcs(startFileTs, Clock::now()) << "mcs"; + } continue; } - uint32_t uncompressedLen; - if (!zipFile.get()->getEntryInfo(entry, nullptr, &uncompressedLen, nullptr, nullptr, - nullptr, nullptr)) { + uint32_t uncompressedLen, compressedLen; + if (!zipFile->getEntryInfo(entry, nullptr, &uncompressedLen, &compressedLen, nullptr, + nullptr, nullptr)) { LOG(ERROR) << "Failed to read native lib entry: " << fileName; - success = false; - break; + return false; } // Create new lib file without signature info - incfs::NewFileParams libFileParams{}; - libFileParams.size = uncompressedLen; - libFileParams.signature = {}; - // Metadata of the new lib file is its relative path - IncFsSpan libFileMetadata; - libFileMetadata.data = targetLibPath.c_str(); - libFileMetadata.size = targetLibPath.size(); - libFileParams.metadata = libFileMetadata; + incfs::NewFileParams libFileParams = { + .size = uncompressedLen, + .signature = {}, + // Metadata of the new lib file is its relative path + .metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()}, + }; incfs::FileId libFileId = idFromMetadata(targetLibPath); - if (auto res = makeFile(storage, targetLibPath, 0777, libFileId, libFileParams)) { + if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0777, libFileId, + libFileParams)) { LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res; - success = false; // If one lib file fails to be created, abort others as well - break; + return false; } + + auto makeFileTs = Clock::now(); + // If it is a zero-byte file, skip data writing if (uncompressedLen == 0) { + if (sEnablePerfLogging) { + LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> " + << uncompressedLen << " bytes): " << elapsedMcs(startFileTs, makeFileTs) + << "mcs, make: " << elapsedMcs(startFileTs, makeFileTs); + } continue; } // Write extracted data to new file - std::vector<uint8_t> libData(uncompressedLen); - if (!zipFile.get()->uncompressEntry(entry, &libData[0], uncompressedLen)) { + // NOTE: don't zero-initialize memory, it may take a while + auto libData = std::unique_ptr<uint8_t[]>(new uint8_t[uncompressedLen]); + if (!zipFile->uncompressEntry(entry, libData.get(), uncompressedLen)) { LOG(ERROR) << "Failed to extract native lib zip entry: " << fileName; - success = false; - break; + return false; } + + auto extractFileTs = Clock::now(); + const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId); if (!writeFd.ok()) { LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd; - success = false; - break; + return false; } - const int numBlocks = uncompressedLen / constants().blockSize + 1; - std::vector<IncFsDataBlock> instructions; - auto remainingData = std::span(libData); - for (int i = 0; i < numBlocks - 1; i++) { + + auto openFileTs = Clock::now(); + + const int numBlocks = (uncompressedLen + constants().blockSize - 1) / constants().blockSize; + instructions.clear(); + instructions.reserve(numBlocks); + auto remainingData = std::span(libData.get(), uncompressedLen); + for (int i = 0; i < numBlocks; i++) { + const auto blockSize = std::min<uint16_t>(constants().blockSize, remainingData.size()); auto inst = IncFsDataBlock{ - .fileFd = writeFd, + .fileFd = writeFd.get(), .pageIndex = static_cast<IncFsBlockIndex>(i), .compression = INCFS_COMPRESSION_KIND_NONE, .kind = INCFS_BLOCK_KIND_DATA, - .dataSize = static_cast<uint16_t>(constants().blockSize), + .dataSize = blockSize, .data = reinterpret_cast<const char*>(remainingData.data()), }; instructions.push_back(inst); - remainingData = remainingData.subspan(constants().blockSize); + remainingData = remainingData.subspan(blockSize); } - // Last block - auto inst = IncFsDataBlock{ - .fileFd = writeFd, - .pageIndex = static_cast<IncFsBlockIndex>(numBlocks - 1), - .compression = INCFS_COMPRESSION_KIND_NONE, - .kind = INCFS_BLOCK_KIND_DATA, - .dataSize = static_cast<uint16_t>(remainingData.size()), - .data = reinterpret_cast<const char*>(remainingData.data()), - }; - instructions.push_back(inst); + auto prepareInstsTs = Clock::now(); + size_t res = mIncFs->writeBlocks(instructions); if (res != instructions.size()) { LOG(ERROR) << "Failed to write data into: " << targetLibPath; - success = false; + return false; + } + + if (sEnablePerfLogging) { + auto endFileTs = Clock::now(); + LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> " + << uncompressedLen << " bytes): " << elapsedMcs(startFileTs, endFileTs) + << "mcs, make: " << elapsedMcs(startFileTs, makeFileTs) + << " extract: " << elapsedMcs(makeFileTs, extractFileTs) + << " open: " << elapsedMcs(extractFileTs, openFileTs) + << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs) + << " write:" << elapsedMcs(prepareInstsTs, endFileTs); } - instructions.clear(); } - zipFile.get()->endIteration(cookie); - return success; + + if (sEnablePerfLogging) { + auto end = Clock::now(); + LOG(INFO) << "incfs: configureNativeBinaries complete in " << elapsedMcs(start, end) + << "mcs, make dirs: " << elapsedMcs(start, mkDirsTs) + << " open zip: " << elapsedMcs(mkDirsTs, openZipTs) + << " extract all: " << elapsedMcs(openZipTs, end); + } + + return true; } void IncrementalService::registerAppOpsCallback(const std::string& packageName) { diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 58002974e180..9b156464f480 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -157,6 +157,8 @@ public: }; private: + static const bool sEnablePerfLogging; + struct IncFsMount { struct Bind { StorageId storage; @@ -227,8 +229,10 @@ private: void deleteStorage(IncFsMount& ifs); void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock); MountMap::iterator getStorageSlotLocked(); - std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage, + std::string normalizePathToStorage(const IfsMountPtr& incfs, StorageId storage, std::string_view path); + std::string normalizePathToStorageLocked(IncFsMount::StorageMap::iterator storageIt, + std::string_view path); binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index e5ffbacb357b..e2a247394a81 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -136,6 +136,7 @@ import com.android.server.pm.OtaDexoptService; import com.android.server.pm.PackageManagerService; import com.android.server.pm.ShortcutService; import com.android.server.pm.UserManagerService; +import com.android.server.pm.dex.SystemServerDexLoadReporter; import com.android.server.policy.PermissionPolicyService; import com.android.server.policy.PhoneWindowManager; import com.android.server.policy.role.LegacyRoleResolutionPolicy; @@ -837,6 +838,11 @@ public final class SystemServer { Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain"); } + // Now that the package manager has started, register the dex load reporter to capture any + // dex files loaded by system server. + // These dex files will be optimized by the BackgroundDexOptService. + SystemServerDexLoadReporter.configureSystemServerDexReporter(mPackageManagerService); + mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager(); t.traceEnd(); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index d69e1b8786b4..8398585ca74a 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -85,6 +85,9 @@ public class DexManagerTests { private TestData mBarUser0UnsupportedClassLoader; private TestData mBarUser0DelegateLastClassLoader; + private TestData mSystemServerJar; + private TestData mSystemServerJarInvalid; + private int mUser0; private int mUser1; @@ -108,6 +111,9 @@ public class DexManagerTests { mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0, DELEGATE_LAST_CLASS_LOADER_NAME); + mSystemServerJar = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); + mSystemServerJarInvalid = new TestData("android", isa, mUser0, PATH_CLASS_LOADER_NAME); + mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock); @@ -587,6 +593,25 @@ public class DexManagerTests { assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries); } + + @Test + public void testNotifySystemServerUse() { + List<String> dexFiles = new ArrayList<String>(); + dexFiles.add("/system/framework/foo"); + notifyDexLoad(mSystemServerJar, dexFiles, mUser0); + PackageUseInfo pui = getPackageUseInfo(mSystemServerJar); + assertIsUsedByOtherApps(mSystemServerJar, pui, false); + } + + @Test + public void testNotifySystemServerInvalidUse() { + List<String> dexFiles = new ArrayList<String>(); + dexFiles.add("/data/foo"); + notifyDexLoad(mSystemServerJarInvalid, dexFiles, mUser0); + assertNoUseInfo(mSystemServerJarInvalid); + assertNoDclInfo(mSystemServerJarInvalid); + } + private void assertSecondaryUse(TestData testData, PackageUseInfo pui, List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId, String[] expectedContexts) { diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index 89bc65b5a44d..b6eb9010ce90 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -30,7 +30,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -41,7 +40,6 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; import android.platform.test.annotations.Presubmit; import android.util.IntArray; @@ -49,7 +47,6 @@ import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.test.InsetsModeSession; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.AfterClass; @@ -168,6 +165,18 @@ public class InsetsPolicyTest extends WindowTestsBase { } @Test + public void testControlsForDispatch_forceShowSystemBarsFromExternal_appHasNoControl() { + mDisplayContent.getDisplayPolicy().setForceShowSystemBars(true); + addWindow(TYPE_STATUS_BAR, "statusBar"); + addWindow(TYPE_NAVIGATION_BAR, "navBar"); + + final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch(); + + // The focused app window cannot control system bars. + assertNull(controls); + } + + @Test public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() { addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar") .getControllableInsetProvider().getSource().setVisible(false); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index add4e9cf3948..ea933dfe42dc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1124,8 +1124,6 @@ public class RecentTasksTest extends ActivityTestsBase { } }); assertSecurityException(expectCallable, - () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true)); - assertSecurityException(expectCallable, () -> mService.startActivityFromRecents(0, new Bundle())); assertSecurityException(expectCallable, () -> mService.getTaskSnapshot(0, true)); assertSecurityException(expectCallable, () -> mService.registerTaskStackListener(null)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 6d2b7b1e86fe..f19550ced0bf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -23,7 +23,6 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; @@ -318,7 +317,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { // Assume activity transition should animate when no // IRecentsAnimationController#setDeferCancelUntilNextTransition called. assertFalse(mController.shouldDeferCancelWithScreenshot()); - assertTrue(activity.shouldAnimate(TRANSIT_ACTIVITY_CLOSE)); + assertTrue(activity.shouldAnimate()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 1ca2e318b0d7..397f73c3e67c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -134,6 +134,7 @@ class WindowTestsBase extends SystemServiceTestsBase { mChildAppWindowBelow = createCommonWindow(mAppWindow, TYPE_APPLICATION_MEDIA_OVERLAY, "mChildAppWindowBelow"); + mDisplayContent.getDisplayPolicy().setForceShowSystemBars(false); } // Adding a display will cause freezing the display. Make sure to wait until it's diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java index f3e9de0d2688..3048ad7c1fb0 100644 --- a/telephony/common/android/telephony/LocationAccessPolicy.java +++ b/telephony/common/android/telephony/LocationAccessPolicy.java @@ -311,7 +311,7 @@ public final class LocationAccessPolicy { } // If the user or profile is current, permission is granted. // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. - return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid); + return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, pid, uid); } private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) { |