diff options
198 files changed, 3509 insertions, 1621 deletions
diff --git a/api/test-current.txt b/api/test-current.txt index 529dcf71ef6e..5741fe7e90a4 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5579,7 +5579,7 @@ package android.window { method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo); method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer(); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static void setLaunchRoot(int, @NonNull android.window.WindowContainerToken); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer(); } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index cee607fd7428..fef8d1005e29 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1924,10 +1924,8 @@ class ContextImpl extends Context { @Override public Object getSystemService(String name) { if (vmIncorrectContextUseEnabled()) { - // We may override this API from outer context. - final boolean isUiContext = isUiContext() || isOuterUiContext(); // Check incorrect Context usage. - if (isUiComponent(name) && !isUiContext) { + if (isUiComponent(name) && !isSelfOrOuterUiContext()) { final String errorMessage = "Tried to access visual service " + SystemServiceRegistry.getSystemServiceClassName(name) + " from a non-visual Context:" + getOuterContext(); @@ -1944,15 +1942,17 @@ class ContextImpl extends Context { return SystemServiceRegistry.getSystemService(this, name); } - private boolean isOuterUiContext() { - return getOuterContext() != null && getOuterContext().isUiContext(); - } - @Override public String getSystemServiceName(Class<?> serviceClass) { return SystemServiceRegistry.getSystemServiceName(serviceClass); } + // TODO(b/149463653): check if we still need this method after migrating IMS to WindowContext. + private boolean isSelfOrOuterUiContext() { + // We may override outer context's isUiContext + return isUiContext() || getOuterContext() != null && getOuterContext().isUiContext(); + } + /** @hide */ @Override public boolean isUiContext() { @@ -2413,7 +2413,7 @@ class ContextImpl extends Context { context.setResources(createResources(mToken, mPackageInfo, mSplitName, overrideDisplayId, overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(), mResources.getLoaders())); - context.mIsUiContext = isUiContext() || isOuterUiContext(); + context.mIsUiContext = isSelfOrOuterUiContext(); return context; } @@ -2529,9 +2529,9 @@ class ContextImpl extends Context { @Override public Display getDisplay() { - if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay) { + if (!mIsSystemOrSystemUiContext && !mIsAssociatedWithDisplay && !isSelfOrOuterUiContext()) { throw new UnsupportedOperationException("Tried to obtain display from a Context not " - + "associated with one. Only visual Contexts (such as Activity or one created " + + "associated with one. Only visual Contexts (such as Activity or one created " + "with Context#createWindowContext) or ones created with " + "Context#createDisplayContext are associated with displays. Other types of " + "Contexts are typically related to background entities and may return an " diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index c61426d5c172..98de85d9735d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -9751,21 +9751,6 @@ public class DevicePolicyManager { } /** - * @hide - * Return if this user is a system-only user. An admin can manage a device from a system only - * user by calling {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE}. - * @param admin Which device owner this request is associated with. - * @return if this user is a system-only user. - */ - public boolean isSystemOnlyUser(@NonNull ComponentName admin) { - try { - return mService.isSystemOnlyUser(admin); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** * Called by device owner, or profile owner on organization-owned device, to get the MAC * address of the Wi-Fi device. * diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 9c6a274ccf8c..1c7b617e6d9a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -345,7 +345,6 @@ interface IDevicePolicyManager { void setKeepUninstalledPackages(in ComponentName admin, in String callerPackage, in List<String> packageList); List<String> getKeepUninstalledPackages(in ComponentName admin, in String callerPackage); boolean isManagedProfile(in ComponentName admin); - boolean isSystemOnlyUser(in ComponentName admin); String getWifiMacAddress(in ComponentName admin); void reboot(in ComponentName admin); diff --git a/core/java/android/companion/TEST_MAPPING b/core/java/android/companion/TEST_MAPPING new file mode 100644 index 000000000000..63f54fa35158 --- /dev/null +++ b/core/java/android/companion/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "CtsOsTestCases", + "options": [ + { + "include-filter": "android.os.cts.CompanionDeviceManagerTest" + } + ] + } + ] +} diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index da8d15af92b8..d672c6ac6c2f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -788,7 +788,6 @@ public abstract class PackageManager { INSTALL_ENABLE_ROLLBACK, INSTALL_ALLOW_DOWNGRADE, INSTALL_STAGED, - INSTALL_DRY_RUN, }) @Retention(RetentionPolicy.SOURCE) public @interface InstallFlags {} @@ -966,14 +965,6 @@ public abstract class PackageManager { */ public static final int INSTALL_STAGED = 0x00200000; - /** - * Flag parameter for {@link #installPackage} to indicate that package should only be verified - * but not installed. - * - * @hide - */ - public static final int INSTALL_DRY_RUN = 0x00800000; - /** @hide */ @IntDef(flag = true, value = { DONT_KILL_APP, diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index bd909c7a3f59..192470e964e0 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -43,11 +43,11 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; -import libcore.io.IoUtils; - import com.google.android.collect.Lists; import com.google.android.collect.Maps; +import libcore.io.IoUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -793,7 +793,7 @@ public abstract class RegisteredServicesCache<V> { @VisibleForTesting protected List<UserInfo> getUsers() { - return UserManager.get(mContext).getUsers(true); + return UserManager.get(mContext).getAliveUsers(); } @VisibleForTesting diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 7250801eec81..55afefed734e 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -1210,15 +1210,19 @@ public class InputMethodService extends AbstractInputMethodService { mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true); // IME layout should always be inset by navigation bar, no matter its current visibility, - // unless automotive requests it, since automotive may hide the navigation bar. + // unless automotive requests it. Automotive devices may request the navigation bar to be + // hidden when the IME shows up (controlled via config_automotiveHideNavBarForKeyboard) + // in order to maximize the visible screen real estate. When this happens, the IME window + // should animate from the bottom of the screen to reduce the jank that happens from the + // lack of synchronization between the bottom system window and the IME window. + if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) { + mWindow.getWindow().setDecorFitsSystemWindows(false); + } mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener( (v, insets) -> v.onApplyWindowInsets( new WindowInsets.Builder(insets).setInsets( navigationBars(), - mIsAutomotive && mAutomotiveHideNavBarForKeyboard - ? android.graphics.Insets.NONE - : insets.getInsetsIgnoringVisibility(navigationBars()) - ) + insets.getInsetsIgnoringVisibility(navigationBars())) .build())); // For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set diff --git a/core/java/android/inputmethodservice/TEST_MAPPING b/core/java/android/inputmethodservice/TEST_MAPPING new file mode 100644 index 000000000000..0ccd75dcbdce --- /dev/null +++ b/core/java/android/inputmethodservice/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/base/core/java/android/view/inputmethod" + } + ] +} diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 81a147c68e2e..81ffefd05b19 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1293,7 +1293,7 @@ public class UserManager { * in {@link UserManager} & {@link DevicePolicyManager}. * Note: This is slightly different from the real set of user restrictions listed in {@link * com.android.server.pm.UserRestrictionsUtils#USER_RESTRICTIONS}. For example - * {@link #KEY_RESTRICTIONS_PENDING} is not a real user restriction, but is a a legitimate + * {@link #KEY_RESTRICTIONS_PENDING} is not a real user restriction, but is a legitimate * value that can be passed into {@link #hasUserRestriction(String)}. * @hide */ @@ -3252,7 +3252,8 @@ public class UserManager { @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public @NonNull List<UserHandle> getUserHandles(boolean excludeDying) { - List<UserInfo> users = getUsers(excludeDying); + List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying, + /* excludePreCreated= */ true); List<UserHandle> result = new ArrayList<>(users.size()); for (UserInfo user : users) { result.add(user.getUserHandle()); @@ -3270,7 +3271,8 @@ public class UserManager { @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean excludeDying) { - List<UserInfo> users = getUsers(excludeDying); + List<UserInfo> users = getUsers(/* excludePartial= */ true, excludeDying, + /* excludePreCreated= */ true); long[] result = new long[users.size()]; for (int i = 0; i < result.length; i++) { result[i] = users.get(i).serialNumber; @@ -3336,7 +3338,7 @@ public class UserManager { public boolean canAddMoreUsers() { // TODO(b/142482943): UMS has different logic, excluding Demo and Profile from counting. Why // not here? The logic is inconsistent. See UMS.canAddMoreManagedProfiles - final List<UserInfo> users = getUsers(true); + final List<UserInfo> users = getAliveUsers(); final int totalUserCount = users.size(); int aliveUserCount = 0; for (int i = 0; i < totalUserCount; i++) { @@ -4144,7 +4146,7 @@ public class UserManager { /** Returns whether there are any users (other than the current user) to which to switch. */ private boolean areThereUsersToWhichToSwitch() { - final List<UserInfo> users = getUsers(true); + final List<UserInfo> users = getAliveUsers(); if (users == null) { return false; } diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl index be8b9297ea6a..f351c7df8d5d 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -90,6 +90,14 @@ interface IIncrementalService { int unlink(int storageId, in @utf8InCpp String path); /** + * Checks if a file is fully loaded. File is specified by its path. + * 0 - fully loaded + * >0 - certain pages missing + * <0 - -errcode + */ + int isFileFullyLoaded(int storageId, in @utf8InCpp String path); + + /** * Returns overall loading progress of all the files on a storage, progress value between [0,1]. * Returns a negative value on error. */ diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index b8dbfbb9d109..ed386f79882f 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -304,6 +304,25 @@ public final class IncrementalStorage { } /** + * Checks whether a file under the current storage directory is fully loaded. + * + * @param path The relative path of the file. + * @return True if the file is fully loaded. + */ + public boolean isFileFullyLoaded(@NonNull String path) throws IOException { + try { + int res = mService.isFileFullyLoaded(mId, path); + if (res < 0) { + throw new IOException("isFileFullyLoaded() failed, errno " + -res); + } + return res == 0; + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return false; + } + } + + /** * Returns the loading progress of a storage * * @return progress value between [0, 1]. diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 276f16216b4d..c3b6d8e2cfe3 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -870,7 +870,7 @@ public class CallLog { // Otherwise, insert to all other users that are running and unlocked. - final List<UserInfo> users = userManager.getUsers(true); + final List<UserInfo> users = userManager.getAliveUsers(); final int count = users.size(); for (int i = 0; i < count; i++) { diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 327bca268a7b..d55fc511fc77 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -218,8 +218,15 @@ public abstract class DocumentsProvider extends ContentProvider { } /** {@hide} */ - private void enforceTree(Uri documentUri) { - if (isTreeUri(documentUri)) { + private void enforceTreeForExtraUris(Bundle extras) { + enforceTree(extras.getParcelable(DocumentsContract.EXTRA_URI)); + enforceTree(extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI)); + enforceTree(extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI)); + } + + /** {@hide} */ + private void enforceTree(@Nullable Uri documentUri) { + if (documentUri != null && isTreeUri(documentUri)) { final String parent = getTreeDocumentId(documentUri); final String child = getDocumentId(documentUri); if (Objects.equals(parent, child)) { @@ -1076,6 +1083,9 @@ public abstract class DocumentsProvider extends ContentProvider { final Context context = getContext(); final Bundle out = new Bundle(); + // If the URI is a tree URI performs some validation. + enforceTreeForExtraUris(extras); + if (METHOD_EJECT_ROOT.equals(method)) { // Given that certain system apps can hold MOUNT_UNMOUNT permission, but only apps // signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for @@ -1099,9 +1109,6 @@ public abstract class DocumentsProvider extends ContentProvider { "Requested authority " + authority + " doesn't match provider " + mAuthority); } - // If the URI is a tree URI performs some validation. - enforceTree(documentUri); - if (METHOD_IS_CHILD_DOCUMENT.equals(method)) { enforceReadPermissionInner(documentUri, getCallingPackage(), getCallingAttributionTag(), null); diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index b80718018652..cbc304b3293a 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -950,10 +950,6 @@ public class PhoneStateListener { * This method will be called when an emergency call is placed on any subscription (including * the no-SIM case), regardless of which subscription this listener was registered on. * - * This method is deprecated. Both this method and the new - * {@link #onOutgoingEmergencyCall(EmergencyNumber, int)} will be called when an outgoing - * emergency call is placed. - * * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to. * * @deprecated Use {@link #onOutgoingEmergencyCall(EmergencyNumber, int)}. @@ -972,22 +968,24 @@ public class PhoneStateListener { * This method will be called when an emergency call is placed on any subscription (including * the no-SIM case), regardless of which subscription this listener was registered on. * - * Both this method and the deprecated {@link #onOutgoingEmergencyCall(EmergencyNumber)} will be - * called when an outgoing emergency call is placed. You should only implement one of these - * methods. + * The default implementation of this method calls + * {@link #onOutgoingEmergencyCall(EmergencyNumber)} for backwards compatibility purposes. Do + * not call {@code super(...)} from within your implementation unless you want + * {@link #onOutgoingEmergencyCall(EmergencyNumber)} to be called as well. * * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to. * @param subscriptionId The subscription ID used to place the emergency call. If the * emergency call was placed without a valid subscription (e.g. when there * are no SIM cards in the device), this will be equal to * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. - * * @hide */ @SystemApi @TestApi public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, int subscriptionId) { + // Default implementation for backwards compatibility + onOutgoingEmergencyCall(placedEmergencyNumber); } /** @@ -1375,10 +1373,6 @@ public class PhoneStateListener { Binder.withCleanCallingIdentity( () -> mExecutor.execute( - () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber))); - - Binder.withCleanCallingIdentity( - () -> mExecutor.execute( () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber, subscriptionId))); } diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java index 054dff726ca1..32cc30be8de4 100644 --- a/core/java/android/view/FrameMetrics.java +++ b/core/java/android/view/FrameMetrics.java @@ -250,8 +250,11 @@ public final class FrameMetrics { Index.INTENDED_VSYNC, Index.FRAME_COMPLETED, }; + /** + * @hide + */ @UnsupportedAppUsage - /* package */ final long[] mTimingData; + public final long[] mTimingData; /** * Constructs a FrameMetrics object as a copy. @@ -270,7 +273,7 @@ public final class FrameMetrics { /** * @hide */ - FrameMetrics() { + public FrameMetrics() { mTimingData = new long[Index.FRAME_STATS_COUNT]; } diff --git a/core/java/android/view/inputmethod/TEST_MAPPING b/core/java/android/view/inputmethod/TEST_MAPPING new file mode 100644 index 000000000000..4b2ea1a096c8 --- /dev/null +++ b/core/java/android/view/inputmethod/TEST_MAPPING @@ -0,0 +1,18 @@ +{ + "presubmit": [ + { + "name": "CtsAutoFillServiceTestCases", + "options": [ + { + "include-filter": "android.autofillservice.cts.inline" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.AppModeFull" + } + ] + } + ] +} diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index 92fa80e40caf..12b16ff6645c 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -60,5 +60,6 @@ interface ITaskOrganizerController { * Requests that the given task organizer is notified when back is pressed on the root activity * of one of its controlled tasks. */ - void setInterceptBackPressedOnTaskRoot(ITaskOrganizer organizer, boolean interceptBackPressed); + void setInterceptBackPressedOnTaskRoot(in WindowContainerToken task, + boolean interceptBackPressed); } diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 7ec4f99ce959..38fb023a0822 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -149,9 +149,10 @@ public class TaskOrganizer extends WindowOrganizer { * of one of its controlled tasks. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public void setInterceptBackPressedOnTaskRoot(boolean interceptBackPressed) { + public void setInterceptBackPressedOnTaskRoot(@NonNull WindowContainerToken task, + boolean interceptBackPressed) { try { - getController().setInterceptBackPressedOnTaskRoot(mInterface, interceptBackPressed); + getController().setInterceptBackPressedOnTaskRoot(task, interceptBackPressed); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java index 46c72f88e14b..eb9dfed7f644 100644 --- a/core/java/android/window/TaskOrganizerTaskEmbedder.java +++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java @@ -74,7 +74,7 @@ public class TaskOrganizerTaskEmbedder extends TaskEmbedder { // windowing mode tasks. Plan is to migrate this to a wm-shell front-end when that // infrastructure is ready. // mTaskOrganizer.registerOrganizer(); - mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true); + // mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true); return super.onInitialize(); } diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java new file mode 100644 index 000000000000..f9a2ecc10dc8 --- /dev/null +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -0,0 +1,234 @@ +/* + * 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.internal.jank; + +import android.annotation.NonNull; +import android.graphics.HardwareRendererObserver; +import android.os.Handler; +import android.os.Trace; +import android.util.Log; +import android.view.FrameMetrics; +import android.view.ThreadedRenderer; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.jank.InteractionJankMonitor.Session; + +/** + * @hide + */ +public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvailableListener { + private static final String TAG = FrameTracker.class.getSimpleName(); + private static final boolean DEBUG = false; + //TODO (163431584): need also consider other refresh rates. + private static final long CRITERIA = 1000000000 / 60; + @VisibleForTesting + public static final long UNKNOWN_TIMESTAMP = -1; + + @VisibleForTesting + public long mBeginTime = UNKNOWN_TIMESTAMP; + @VisibleForTesting + public long mEndTime = UNKNOWN_TIMESTAMP; + public boolean mShouldTriggerTrace; + public HardwareRendererObserver mObserver; + public ThreadedRendererWrapper mRendererWrapper; + public FrameMetricsWrapper mMetricsWrapper; + + private Session mSession; + + public FrameTracker(@NonNull Session session, + @NonNull Handler handler, @NonNull ThreadedRenderer renderer) { + mSession = session; + mRendererWrapper = new ThreadedRendererWrapper(renderer); + mMetricsWrapper = new FrameMetricsWrapper(); + mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler); + } + + /** + * This constructor is only for unit tests. + * @param session a trace session. + * @param renderer a test double for ThreadedRenderer + * @param metrics a test double for FrameMetrics + */ + @VisibleForTesting + public FrameTracker(@NonNull Session session, Handler handler, + @NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) { + mSession = session; + mRendererWrapper = renderer; + mMetricsWrapper = metrics; + mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler); + } + + /** + * Begin a trace session of the CUJ. + */ + public void begin() { + long timestamp = System.nanoTime(); + if (DEBUG) { + Log.d(TAG, "begin: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime + + ", end(ns)=" + mEndTime + ", session=" + mSession); + } + if (mBeginTime != UNKNOWN_TIMESTAMP && mEndTime == UNKNOWN_TIMESTAMP) { + // We have an ongoing tracing already, skip subsequent calls. + return; + } + mBeginTime = timestamp; + mEndTime = UNKNOWN_TIMESTAMP; + Trace.beginAsyncSection(mSession.getName(), (int) mBeginTime); + mRendererWrapper.addObserver(mObserver); + } + + /** + * End the trace session of the CUJ. + */ + public void end() { + long timestamp = System.nanoTime(); + if (DEBUG) { + Log.d(TAG, "end: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime + + ", end(ns)=" + mEndTime + ", session=" + mSession); + } + if (mBeginTime == UNKNOWN_TIMESTAMP || mEndTime != UNKNOWN_TIMESTAMP) { + // We haven't started a trace yet. + return; + } + mEndTime = timestamp; + Trace.endAsyncSection(mSession.getName(), (int) mBeginTime); + } + + /** + * Check if we had a janky frame according to the metrics. + * @param metrics frame metrics + * @return true if it is a janky frame + */ + @VisibleForTesting + public boolean isJankyFrame(FrameMetricsWrapper metrics) { + long totalDurationMs = metrics.getMetric(FrameMetrics.TOTAL_DURATION); + boolean isFirstFrame = metrics.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1; + boolean isJanky = !isFirstFrame && totalDurationMs - CRITERIA > 0; + + if (DEBUG) { + StringBuilder sb = new StringBuilder(); + sb.append(isJanky).append(","); + sb.append(metrics.getMetric(FrameMetrics.FIRST_DRAW_FRAME)).append(","); + sb.append(metrics.getMetric(FrameMetrics.INPUT_HANDLING_DURATION)).append(","); + sb.append(metrics.getMetric(FrameMetrics.ANIMATION_DURATION)).append(","); + sb.append(metrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION)).append(","); + sb.append(metrics.getMetric(FrameMetrics.DRAW_DURATION)).append(","); + sb.append(metrics.getMetric(FrameMetrics.SYNC_DURATION)).append(","); + sb.append(metrics.getMetric(FrameMetrics.COMMAND_ISSUE_DURATION)).append(","); + sb.append(metrics.getMetric(FrameMetrics.SWAP_BUFFERS_DURATION)).append(","); + sb.append(totalDurationMs).append(","); + sb.append(metrics.getMetric(FrameMetrics.INTENDED_VSYNC_TIMESTAMP)).append(","); + sb.append(metrics.getMetric(FrameMetrics.VSYNC_TIMESTAMP)).append(","); + Log.v(TAG, "metrics=" + sb.toString()); + } + + return isJanky; + } + + @Override + public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { + // Since this callback might come a little bit late after the end() call. + // We should keep tracking the begin / end timestamp. + // Then compare with vsync timestamp to check if the frame is in the duration of the CUJ. + + if (mBeginTime == UNKNOWN_TIMESTAMP) return; // We haven't started tracing yet. + long vsyncTimestamp = mMetricsWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP); + if (vsyncTimestamp < mBeginTime) return; // The tracing has been started. + + // If the end time has not been set, we are still in the tracing. + if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime) { + // The tracing has been ended, remove the observer, see if need to trigger perfetto. + mRendererWrapper.removeObserver(mObserver); + // Trigger perfetto if necessary. + if (mShouldTriggerTrace) { + if (DEBUG) { + Log.v(TAG, "Found janky frame, triggering perfetto."); + } + triggerPerfetto(); + } + return; + } + + // The frame is in the duration of the CUJ, check if it catches the deadline. + if (isJankyFrame(mMetricsWrapper)) { + mShouldTriggerTrace = true; + } + } + + /** + * Trigger the prefetto daemon. + */ + @VisibleForTesting + public void triggerPerfetto() { + InteractionJankMonitor.trigger(); + } + + /** + * A wrapper class that we can spy FrameMetrics (a final class) in unit tests. + */ + public static class FrameMetricsWrapper { + private FrameMetrics mFrameMetrics; + + public FrameMetricsWrapper() { + mFrameMetrics = new FrameMetrics(); + } + + /** + * Wrapper method. + * @return timing data of the metrics + */ + public long[] getTiming() { + return mFrameMetrics.mTimingData; + } + + /** + * Wrapper method. + * @param index specific index of the timing data + * @return the timing data of the specified index + */ + public long getMetric(int index) { + return mFrameMetrics.getMetric(index); + } + } + + /** + * A wrapper class that we can spy ThreadedRenderer (a final class) in unit tests. + */ + public static class ThreadedRendererWrapper { + private ThreadedRenderer mRenderer; + + public ThreadedRendererWrapper(ThreadedRenderer renderer) { + mRenderer = renderer; + } + + /** + * Wrapper method. + * @param observer observer + */ + public void addObserver(HardwareRendererObserver observer) { + mRenderer.addObserver(observer); + } + + /** + * Wrapper method. + * @param observer observer + */ + public void removeObserver(HardwareRendererObserver observer) { + mRenderer.removeObserver(observer); + } + } +} diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java new file mode 100644 index 000000000000..6bfb178bc102 --- /dev/null +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -0,0 +1,181 @@ +/* + * 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.internal.jank; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.HandlerThread; +import android.view.ThreadedRenderer; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.HashMap; +import java.util.Map; + +/** + * This class let users to begin and end the always on tracing mechanism. + * @hide + */ +public class InteractionJankMonitor { + private static final String TAG = InteractionJankMonitor.class.getSimpleName(); + private static final boolean DEBUG = false; + private static final Object LOCK = new Object(); + public static final int CUJ_NOTIFICATION_SHADE_MOTION = 0; + public static final int CUJ_NOTIFICATION_SHADE_GESTURE = 1; + + private static ThreadedRenderer sRenderer; + private static Map<String, FrameTracker> sRunningTracker; + private static HandlerThread sWorker; + private static boolean sInitialized; + + /** @hide */ + @IntDef({ + CUJ_NOTIFICATION_SHADE_MOTION, + CUJ_NOTIFICATION_SHADE_GESTURE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface CujType {} + + /** + * @param view Any view in the view tree to get context and ThreadedRenderer. + */ + public static void init(@NonNull View view) { + init(view, null, null, null); + } + + /** + * Should be only invoked internally or from unit tests. + */ + @VisibleForTesting + public static void init(@NonNull View view, @NonNull ThreadedRenderer renderer, + @NonNull Map<String, FrameTracker> map, @NonNull HandlerThread worker) { + //TODO (163505250): This should be no-op if not in droid food rom. + synchronized (LOCK) { + if (!sInitialized) { + if (!view.isAttachedToWindow()) { + throw new IllegalStateException("View is not attached!"); + } + sRenderer = renderer == null ? view.getThreadedRenderer() : renderer; + sRunningTracker = map == null ? new HashMap<>() : map; + sWorker = worker == null ? new HandlerThread("Aot-Worker") : worker; + sWorker.start(); + sInitialized = true; + } + } + } + + /** + * Must invoke init() before invoking this method. + */ + public static void begin(@NonNull @CujType int cujType) { + begin(cujType, null); + } + + /** + * Should be only invoked internally or from unit tests. + */ + @VisibleForTesting + public static void begin(@NonNull @CujType int cujType, FrameTracker tracker) { + //TODO (163505250): This should be no-op if not in droid food rom. + //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads. + synchronized (LOCK) { + checkInitStateLocked(); + Session session = new Session(cujType); + FrameTracker currentTracker = getTracker(session.getName()); + if (currentTracker != null) return; + if (tracker == null) { + tracker = new FrameTracker(session, sWorker.getThreadHandler(), sRenderer); + } + sRunningTracker.put(session.getName(), tracker); + tracker.begin(); + } + } + + /** + * Must invoke init() before invoking this method. + */ + public static void end(@NonNull @CujType int cujType) { + //TODO (163505250): This should be no-op if not in droid food rom. + //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads. + synchronized (LOCK) { + checkInitStateLocked(); + Session session = new Session(cujType); + FrameTracker tracker = getTracker(session.getName()); + if (tracker != null) { + tracker.end(); + sRunningTracker.remove(session.getName()); + } + } + } + + private static void checkInitStateLocked() { + if (!sInitialized) { + throw new IllegalStateException("InteractionJankMonitor not initialized!"); + } + } + + /** + * Should be only invoked from unit tests. + */ + @VisibleForTesting + public static void reset() { + sInitialized = false; + sRenderer = null; + sRunningTracker = null; + if (sWorker != null) { + sWorker.quit(); + sWorker = null; + } + } + + private static FrameTracker getTracker(String sessionName) { + synchronized (LOCK) { + return sRunningTracker.get(sessionName); + } + } + + /** + * Trigger the perfetto daemon to collect and upload data. + */ + public static void trigger() { + sWorker.getThreadHandler().post( + () -> PerfettoTrigger.trigger(PerfettoTrigger.TRIGGER_TYPE_JANK)); + } + + /** + * A class to represent a session. + */ + public static class Session { + private @CujType int mId; + + public Session(@CujType int session) { + mId = session; + } + + public int getId() { + return mId; + } + + public String getName() { + return "CujType<" + mId + ">"; + } + } + +} diff --git a/core/java/com/android/internal/jank/PerfettoTrigger.java b/core/java/com/android/internal/jank/PerfettoTrigger.java new file mode 100644 index 000000000000..6c8d3cdcf5ae --- /dev/null +++ b/core/java/com/android/internal/jank/PerfettoTrigger.java @@ -0,0 +1,115 @@ +/* + * 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. + */ + +//TODO (165884885): Make PerfettoTrigger more generic and move it to another package. +package com.android.internal.jank; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A trigger implementation with perfetto backend. + * @hide + */ +public class PerfettoTrigger { + private static final String TAG = PerfettoTrigger.class.getSimpleName(); + private static final boolean DEBUG = false; + private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto"; + private static final String[] TRIGGER_TYPE_NAMES = new String[] { "jank-tracker" }; + public static final int TRIGGER_TYPE_JANK = 0; + + /** @hide */ + @IntDef({ + TRIGGER_TYPE_JANK + }) + @Retention(RetentionPolicy.SOURCE) + public @interface TriggerType {} + + /** + * @param type the trigger type + */ + public static void trigger(@NonNull @TriggerType int type) { + try { + Token token = new Token(type, TRIGGER_TYPE_NAMES[type]); + ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, token.getName()); + if (DEBUG) { + StringBuilder sb = new StringBuilder(); + for (String arg : pb.command()) { + sb.append(arg).append(" "); + } + Log.d(TAG, "Triggering " + sb.toString()); + } + Process process = pb.start(); + if (DEBUG) { + readConsoleOutput(process); + } + } catch (IOException | InterruptedException e) { + Log.w(TAG, "Failed to trigger " + type, e); + } + } + + private static void readConsoleOutput(@NonNull Process process) + throws IOException, InterruptedException { + process.waitFor(); + try (BufferedReader errReader = + new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + StringBuilder errLine = new StringBuilder(); + String line; + while ((line = errReader.readLine()) != null) { + errLine.append(line).append("\n"); + } + errLine.append(", code=").append(process.exitValue()); + Log.d(TAG, "err message=" + errLine.toString()); + } + } + + /** + * Token which is used to trigger perfetto. + */ + public static class Token { + private int mType; + private String mName; + + Token(@TriggerType int type, String name) { + mType = type; + mName = name; + } + + /** + * Get trigger type. + * @return trigger type, should be @TriggerType + */ + public int getType() { + return mType; + } + + /** + * Get name of this token as the argument while triggering perfetto. + * @return name + */ + public String getName() { + return mName; + } + } + +} diff --git a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java index 9f100bd6440f..a92e978b2fc1 100644 --- a/core/java/com/android/internal/view/ScrollCaptureViewHelper.java +++ b/core/java/com/android/internal/view/ScrollCaptureViewHelper.java @@ -26,6 +26,36 @@ interface ScrollCaptureViewHelper<V extends View> { int DOWN = 1; /** + * Contains the result of a scroll request. + */ + class ScrollResult { + /** + * The area requested in pixels, within {@link #onComputeScrollBounds scroll bounds}, with + * top/bottom relative to the scroll position at the start of capture. + */ + public Rect requestedArea; + /** + * The area, in pixels of the request which is visible and available for capture. In the + * same coordinate space as {@link #requestedArea}. + */ + public Rect availableArea; + /** + * The updated scroll delta (the relative distance, in pixels that the scroll position has + * moved from the starting position since capture started). + */ + public int scrollDelta; // visible top offset from start + + @Override + public String toString() { + return "ScrollResult{" + + "requestedArea=" + requestedArea + + ", availableArea=" + availableArea + + ", scrollDelta=" + scrollDelta + + '}'; + } + } + + /** * Verifies that the view is still visible and scrollable. If true is returned here, expect a * call to {@link #onComputeScrollBounds(View)} to follow. * @@ -48,6 +78,7 @@ interface ScrollCaptureViewHelper<V extends View> { view.getWidth() - view.getPaddingRight(), view.getHeight() - view.getPaddingBottom()); } + /** * Adjust the target for capture. * <p> @@ -67,14 +98,14 @@ interface ScrollCaptureViewHelper<V extends View> { * needed and return the resulting rectangle describing the position and bounds of the area * which is visible. * + * @param view the view being captured * @param scrollBounds the area in which scrolling content moves, local to the {@code containing * view} * @param requestRect the area relative to {@code scrollBounds} which describes the location of * content to capture for the request - * @return the visible area within scrollBounds of the requested rectangle, return {@code null} - * in the case of an unrecoverable error condition, to abort the capture process + * @return the result of the request as a {@link ScrollResult} */ - Rect onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect); + ScrollResult onScrollRequested(@NonNull V view, Rect scrollBounds, Rect requestRect); /** * Restore the target after capture. diff --git a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java index 4087eda944e0..7b4f73ffde2b 100644 --- a/core/java/com/android/internal/view/ScrollCaptureViewSupport.java +++ b/core/java/com/android/internal/view/ScrollCaptureViewSupport.java @@ -30,21 +30,24 @@ import android.view.ScrollCaptureSession; import android.view.Surface; import android.view.View; +import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult; + import java.lang.ref.WeakReference; import java.util.function.Consumer; /** - * Provides a ScrollCaptureCallback implementation for to handle arbitrary View-based scrolling - * containers. - * <p> - * To use this class, supply the target view and an implementation of {@ScrollCaptureViewHelper} - * to the callback. + * Provides a base ScrollCaptureCallback implementation to handle arbitrary View-based scrolling + * containers. This class handles the bookkeeping aspects of {@link ScrollCaptureCallback} + * including rendering output using HWUI. Adaptable to any {@link View} using + * {@link ScrollCaptureViewHelper}. * * @param <V> the specific View subclass handled - * @hide + * @see ScrollCaptureViewHelper */ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCallback { + private static final String TAG = "ScrollCaptureViewSupport"; + private final WeakReference<V> mWeakView; private final ScrollCaptureViewHelper<V> mViewHelper; private ViewRenderer mRenderer; @@ -52,11 +55,6 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa private boolean mStarted; private boolean mEnded; - static <V extends View> ScrollCaptureCallback createCallback(V view, - ScrollCaptureViewHelper<V> impl) { - return new ScrollCaptureViewSupport<>(view, impl); - } - ScrollCaptureViewSupport(V containingView, ScrollCaptureViewHelper<V> viewHelper) { mWeakView = new WeakReference<>(containingView); mRenderer = new ViewRenderer(); @@ -82,6 +80,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa @Override public final void onScrollCaptureStart(ScrollCaptureSession session, Runnable onReady) { V view = mWeakView.get(); + mEnded = false; mStarted = true; @@ -103,21 +102,30 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa session.notifyBufferSent(0, null); return; } - Rect captureArea = mViewHelper.onScrollRequested(view, session.getScrollBounds(), + // Ask the view to scroll as needed to bring this area into view. + ScrollResult scrollResult = mViewHelper.onScrollRequested(view, session.getScrollBounds(), requestRect); - mRenderer.renderFrame(view, captureArea, mUiHandler, - () -> session.notifyBufferSent(0, captureArea)); + view.invalidate(); // don't wait for vsync + + // For image capture, shift back by scrollDelta to arrive at the location within the view + // where the requested content will be drawn + Rect viewCaptureArea = new Rect(scrollResult.availableArea); + viewCaptureArea.offset(0, -scrollResult.scrollDelta); + + mRenderer.renderView(view, viewCaptureArea, mUiHandler, + (frameNumber) -> session.notifyBufferSent(frameNumber, scrollResult.availableArea)); } @Override public final void onScrollCaptureEnd(Runnable onReady) { V view = mWeakView.get(); if (mStarted && !mEnded) { - mViewHelper.onPrepareForEnd(view); - /* empty */ + if (view != null) { + mViewHelper.onPrepareForEnd(view); + view.invalidate(); + } mEnded = true; - mRenderer.trimMemory(); - mRenderer.setSurface(null); + mRenderer.destroy(); } onReady.run(); } @@ -142,7 +150,7 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa private static final String TAG = "ViewRenderer"; private HardwareRenderer mRenderer; - private RenderNode mRootRenderNode; + private RenderNode mCaptureRenderNode; private final RectF mTempRectF = new RectF(); private final Rect mSourceRect = new Rect(); private final Rect mTempRect = new Rect(); @@ -151,10 +159,14 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa private long mLastRenderedSourceDrawingId = -1; + public interface FrameCompleteListener { + void onFrameComplete(long frameNumber); + } + ViewRenderer() { mRenderer = new HardwareRenderer(); - mRootRenderNode = new RenderNode("ScrollCaptureRoot"); - mRenderer.setContentRoot(mRootRenderNode); + mCaptureRenderNode = new RenderNode("ScrollCaptureRoot"); + mRenderer.setContentRoot(mCaptureRenderNode); // TODO: Figure out a way to flip this on when we are sure the source window is opaque mRenderer.setOpaque(false); @@ -193,18 +205,36 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa // Enable shadows for elevation/Z mRenderer.setLightSourceGeometry(lightX, lightY, lightZ, lightRadius); mRenderer.setLightSourceAlpha(AMBIENT_SHADOW_ALPHA, SPOT_SHADOW_ALPHA); + } + + private void updateRootNode(View source, Rect localSourceRect) { + final View rootView = source.getRootView(); + transformToRoot(source, localSourceRect, mTempRect); + + mCaptureRenderNode.setPosition(0, 0, mTempRect.width(), mTempRect.height()); + RecordingCanvas canvas = mCaptureRenderNode.beginRecording(); + canvas.enableZ(); + canvas.translate(-mTempRect.left, -mTempRect.top); + RenderNode rootViewRenderNode = rootView.updateDisplayListIfDirty(); + if (rootViewRenderNode.hasDisplayList()) { + canvas.drawRenderNode(rootViewRenderNode); + } + mCaptureRenderNode.endRecording(); } - public void renderFrame(View localReference, Rect sourceRect, Handler handler, - Runnable onFrameCommitted) { - if (updateForView(localReference)) { - setupLighting(localReference); + public void renderView(View view, Rect sourceRect, Handler handler, + FrameCompleteListener frameListener) { + if (updateForView(view)) { + setupLighting(view); } - buildRootDisplayList(localReference, sourceRect); + view.invalidate(); + updateRootNode(view, sourceRect); HardwareRenderer.FrameRenderRequest request = mRenderer.createRenderRequest(); request.setVsyncTime(SystemClock.elapsedRealtimeNanos()); - request.setFrameCommitCallback(handler::post, onFrameCommitted); + // private API b/c request.setFrameCommitCallback does not provide access to frameNumber + mRenderer.setFrameCompleteCallback( + frameNr -> handler.post(() -> frameListener.onFrameComplete(frameNr))); request.setWaitForPresent(true); request.syncAndDraw(); } @@ -225,15 +255,5 @@ public class ScrollCaptureViewSupport<V extends View> implements ScrollCaptureCa mTempRectF.round(outRect); } - private void buildRootDisplayList(View source, Rect localSourceRect) { - final View captureSource = source.getRootView(); - transformToRoot(source, localSourceRect, mTempRect); - mRootRenderNode.setPosition(0, 0, mTempRect.width(), mTempRect.height()); - RecordingCanvas canvas = mRootRenderNode.beginRecording(mTempRect.width(), - mTempRect.height()); - canvas.translate(-mTempRect.left, -mTempRect.top); - canvas.drawRenderNode(captureSource.updateDisplayListIfDirty()); - mRootRenderNode.endRecording(); - } } } diff --git a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java index 12bd461f810b..1514b9a285dd 100644 --- a/core/java/com/android/internal/view/ScrollViewCaptureHelper.java +++ b/core/java/com/android/internal/view/ScrollViewCaptureHelper.java @@ -35,13 +35,14 @@ import android.view.ViewParent; * <li>correctly implements {@link ViewParent#requestChildRectangleOnScreen(View, * Rect, boolean)} * </ul> + * + * @see ScrollCaptureViewSupport */ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGroup> { private int mStartScrollY; private boolean mScrollBarEnabled; private int mOverScrollMode; - /** @see ScrollCaptureViewHelper#onPrepareForStart(View, Rect) */ public void onPrepareForStart(@NonNull ViewGroup view, Rect scrollBounds) { mStartScrollY = view.getScrollY(); mOverScrollMode = view.getOverScrollMode(); @@ -54,8 +55,8 @@ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGrou } } - /** @see ScrollCaptureViewHelper#onScrollRequested(View, Rect, Rect) */ - public Rect onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds, Rect requestRect) { + public ScrollResult onScrollRequested(@NonNull ViewGroup view, Rect scrollBounds, + Rect requestRect) { final View contentView = view.getChildAt(0); // returns null, does not throw IOOBE if (contentView == null) { return null; @@ -87,6 +88,9 @@ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGrou \__ Requested Bounds[0,300 - 200,400] (200x100) */ + ScrollResult result = new ScrollResult(); + result.requestedArea = new Rect(requestRect); + // 0) adjust the requestRect to account for scroll change since start // // Scroll Bounds[50,50 - 250,250] (w=200,h=200) @@ -117,8 +121,6 @@ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGrou view.getScrollX() - contentView.getLeft(), view.getScrollY() - contentView.getTop()); - - // requestRect is now local to contentView as requestedContentBounds // contentView (and each parent in turn if possible) will be scrolled // (if necessary) to make all of requestedContent visible, (if possible!) @@ -126,35 +128,37 @@ public class ScrollViewCaptureHelper implements ScrollCaptureViewHelper<ViewGrou // update new offset between starting and current scroll position scrollDelta = view.getScrollY() - mStartScrollY; + result.scrollDelta = scrollDelta; - - // TODO: adjust to avoid occlusions/minimize scroll changes + // TODO: crop capture area to avoid occlusions/minimize scroll changes Point offset = new Point(); - final Rect capturedRect = new Rect(requestedContentBounds); // empty - if (!view.getChildVisibleRect(contentView, capturedRect, offset)) { - capturedRect.setEmpty(); - return capturedRect; + final Rect available = new Rect(requestedContentBounds); // empty + if (!view.getChildVisibleRect(contentView, available, offset)) { + available.setEmpty(); + result.availableArea = available; + return result; } // Transform back from global to content-view local - capturedRect.offset(-offset.x, -offset.y); + available.offset(-offset.x, -offset.y); // Then back to container view - capturedRect.offset( + available.offset( contentView.getLeft() - view.getScrollX(), contentView.getTop() - view.getScrollY()); // And back to relative to scrollBounds - capturedRect.offset(-scrollBounds.left, -scrollBounds.top); + available.offset(-scrollBounds.left, -scrollBounds.top); - // Apply scrollDelta again to return to make capturedRect relative to scrollBounds at + // Apply scrollDelta again to return to make `available` relative to `scrollBounds` at // the scroll position at start of capture. - capturedRect.offset(0, scrollDelta); - return capturedRect; + available.offset(0, scrollDelta); + + result.availableArea = new Rect(available); + return result; } - /** @see ScrollCaptureViewHelper#onPrepareForEnd(View) */ public void onPrepareForEnd(@NonNull ViewGroup view) { view.scrollTo(0, mStartScrollY); if (mOverScrollMode != View.OVER_SCROLL_NEVER) { diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 5c045b65be22..7a5c38385f32 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -20,6 +20,7 @@ #include "android_media_AudioTrack.h" #include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedUtfChars.h> #include "core_jni_helpers.h" #include <utils/Log.h> @@ -251,7 +252,7 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession, jlong nativeAudioTrack, jboolean offload, jint encapsulationMode, - jobject tunerConfiguration) { + jobject tunerConfiguration, jstring opPackageName) { ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d," " nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p", jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes, @@ -337,7 +338,8 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we } // create the native AudioTrack object - lpTrack = new AudioTrack(); + ScopedUtfChars opPackageNameStr(env, opPackageName); + lpTrack = new AudioTrack(opPackageNameStr.c_str()); // read the AudioAttributes values auto paa = JNIAudioAttributeHelper::makeUnique(); @@ -371,23 +373,24 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we status_t status = NO_ERROR; switch (memoryMode) { case MODE_STREAM: - status = lpTrack->set( - AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) - sampleRateInHertz, - format,// word length, PCM - nativeChannelMask, - offload ? 0 : frameCount, - offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE, - audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user) - 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack - 0,// shared mem - true,// thread can call Java - sessionId,// audio session ID - offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC, - offload ? &offloadInfo : NULL, - -1, -1, // default uid, pid values - paa.get()); - + status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed + // in paa (last argument) + sampleRateInHertz, + format, // word length, PCM + nativeChannelMask, offload ? 0 : frameCount, + offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD + : AUDIO_OUTPUT_FLAG_NONE, + audioCallback, + &(lpJniStorage->mCallbackData), // callback, callback data (user) + 0, // notificationFrames == 0 since not using EVENT_MORE_DATA + // to feed the AudioTrack + 0, // shared mem + true, // thread can call Java + sessionId, // audio session ID + offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK + : AudioTrack::TRANSFER_SYNC, + offload ? &offloadInfo : NULL, -1, -1, // default uid, pid values + paa.get()); break; case MODE_STATIC: @@ -398,22 +401,22 @@ static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject we goto native_init_failure; } - status = lpTrack->set( - AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument) - sampleRateInHertz, - format,// word length, PCM - nativeChannelMask, - frameCount, - AUDIO_OUTPUT_FLAG_NONE, - audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)); - 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack - lpJniStorage->mMemBase,// shared mem - true,// thread can call Java - sessionId,// audio session ID - AudioTrack::TRANSFER_SHARED, - NULL, // default offloadInfo - -1, -1, // default uid, pid values - paa.get()); + status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed + // in paa (last argument) + sampleRateInHertz, + format, // word length, PCM + nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE, + audioCallback, + &(lpJniStorage->mCallbackData), // callback, callback data (user) + 0, // notificationFrames == 0 since not using EVENT_MORE_DATA + // to feed the AudioTrack + lpJniStorage->mMemBase, // shared mem + true, // thread can call Java + sessionId, // audio session ID + AudioTrack::TRANSFER_SHARED, + NULL, // default offloadInfo + -1, -1, // default uid, pid values + paa.get()); break; default: @@ -1428,7 +1431,8 @@ static const JNINativeMethod gMethods[] = { {"native_stop", "()V", (void *)android_media_AudioTrack_stop}, {"native_pause", "()V", (void *)android_media_AudioTrack_pause}, {"native_flush", "()V", (void *)android_media_AudioTrack_flush}, - {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;)I", + {"native_setup", + "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;Ljava/lang/String;)I", (void *)android_media_AudioTrack_setup}, {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize}, {"native_release", "()V", (void *)android_media_AudioTrack_release}, diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 34e6efb2bf78..fcf9b837cfa1 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -1261,7 +1261,7 @@ <string name="volume_ringtone" msgid="134784084629229029">"Hlasitost vyzvánění"</string> <string name="volume_music" msgid="7727274216734955095">"Hlasitost médií"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"Přehrávání pomocí rozhraní Bluetooth"</string> - <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"Je nastaven tichý vyzváněcí tón"</string> + <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"Je nastaven tichý vyzvánění"</string> <string name="volume_call" msgid="7625321655265747433">"Hlasitost hovoru"</string> <string name="volume_bluetooth_call" msgid="2930204618610115061">"Hlasitost příchozích hovorů při připojení Bluetooth"</string> <string name="volume_alarm" msgid="4486241060751798448">"Hlasitost budíku"</string> @@ -1272,10 +1272,10 @@ <string name="volume_icon_description_incall" msgid="4491255105381227919">"Hlasitost hovoru"</string> <string name="volume_icon_description_media" msgid="4997633254078171233">"Hlasitost médií"</string> <string name="volume_icon_description_notification" msgid="579091344110747279">"Hlasitost oznámení"</string> - <string name="ringtone_default" msgid="9118299121288174597">"Výchozí vyzváněcí tón"</string> + <string name="ringtone_default" msgid="9118299121288174597">"Výchozí vyzvánění"</string> <string name="ringtone_default_with_actual" msgid="2709686194556159773">"Výchozí (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string> <string name="ringtone_silent" msgid="397111123930141876">"Žádný"</string> - <string name="ringtone_picker_title" msgid="667342618626068253">"Vyzváněcí tóny"</string> + <string name="ringtone_picker_title" msgid="667342618626068253">"Vyzvánění"</string> <string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Zvuky budíku"</string> <string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Zvuky upozornění"</string> <string name="ringtone_unknown" msgid="5059495249862816475">"Neznámé"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 117fecb0ffd0..0b7e396839ef 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -657,7 +657,7 @@ <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"observer netværksforhold"</string> <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Tillader, at en applikation observerer netværksforhold. Bør aldrig være nødvendigt for almindelige apps."</string> <string name="permlab_setInputCalibration" msgid="932069700285223434">"skift kalibrering for inputenheden"</string> - <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Tillader, at appen ændrer kalibreringsparametrene for berøringsskærmen. Dette bør aldrig være nødvendigt for almindelige apps."</string> + <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Tillader, at appen ændrer kalibreringsparametrene for touchskærmen. Dette bør aldrig være nødvendigt for almindelige apps."</string> <string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"få adgang til DRM-certifikater"</string> <string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Tillader, at en applikation provisionerer og anvender DRM-certifikater. Dette bør aldrig være nødvendigt for almindelige apps."</string> <string name="permlab_handoverStatus" msgid="7620438488137057281">"modtag status for Android Beam-overførsler"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index ecce980c613f..c93dbbfda5ef 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -484,8 +484,8 @@ <string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Telefonoaren ordu-zona aldatzeko baimena ematen die aplikazioei."</string> <string name="permlab_getAccounts" msgid="5304317160463582791">"bilatu gailuko kontuak"</string> <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Tabletak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Instalatuta dauzkazun aplikazioek sortutako kontuak har daitezke barnean."</string> - <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Android TV gailuak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Kontu horien artean, instalatuta dituzun aplikazioek sortutako kontuak egon litezke."</string> - <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Telefonoak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Instalatuta dituzun aplikazioek sortutako kontuak har daitezke barnean."</string> + <string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Android TV gailuak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Kontu horien artean, instalatuta dauzkazun aplikazioek sortutako kontuak egon litezke."</string> + <string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Telefonoak ezagutzen dituen kontuen zerrenda lortzeko baimena ematen die aplikazioei. Instalatuta dauzkazun aplikazioek sortutako kontuak har daitezke barnean."</string> <string name="permlab_accessNetworkState" msgid="2349126720783633918">"ikusi sareko konexioak"</string> <string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Sareko konexioei buruzko informazioa ikusteko baimena ematen die aplikazioei; adibidez, zer sare dauden eta zeintzuk dauden konektatuta."</string> <string name="permlab_createNetworkSockets" msgid="3224420491603590541">"izan sarerako sarbide osoa"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index f9e3e2fa97e7..f4fb83deba39 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2035,7 +2035,7 @@ <string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"Aplikasi ini tidak diberi izin merekam, tetapi dapat merekam audio melalui perangkat USB ini."</string> <string name="accessibility_system_action_home_label" msgid="3234748160850301870">"Beranda"</string> <string name="accessibility_system_action_back_label" msgid="4205361367345537608">"Kembali"</string> - <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Aplikasi yang Baru Dipakai"</string> + <string name="accessibility_system_action_recents_label" msgid="4782875610281649728">"Aplikasi Terbaru"</string> <string name="accessibility_system_action_notifications_label" msgid="6083767351772162010">"Notifikasi"</string> <string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"Setelan Cepat"</string> <string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"Dialog Daya"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index bb79c2193a36..ee0362e4a184 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -304,13 +304,13 @@ <string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string> <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string> <string name="permgrouplab_storage" msgid="1938416135375282333">"फाइल र मिडिया"</string> - <string name="permgroupdesc_storage" msgid="6351503740613026600">"तपाईंको यन्त्रमा तस्बिर, मिडिया, र फाइलहरूमाथि पहुँच गर्नुहोस्"</string> + <string name="permgroupdesc_storage" msgid="6351503740613026600">"तपाईंको यन्त्रमा फोटो, मिडिया, र फाइलहरूमाथि पहुँच गर्नुहोस्"</string> <string name="permgrouplab_microphone" msgid="2480597427667420076">"माइक्रोफोन"</string> <string name="permgroupdesc_microphone" msgid="1047786732792487722">"अडियो रेकर्ड गर्नुहोस्"</string> <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"शारीरिक क्रियाकलाप"</string> <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"आफ्नो शारीरिक क्रियाकलापको डेटामाथि पहुँच राख्नु"</string> <string name="permgrouplab_camera" msgid="9090413408963547706">"क्यामेरा"</string> - <string name="permgroupdesc_camera" msgid="7585150538459320326">"तस्बिर खिच्नुका साथै भिडियो रेकर्ड गर्नुहोस्"</string> + <string name="permgroupdesc_camera" msgid="7585150538459320326">"फोटो खिच्नुका साथै भिडियो रेकर्ड गर्नुहोस्"</string> <string name="permgrouplab_calllog" msgid="7926834372073550288">"कलका लगहरू"</string> <string name="permgroupdesc_calllog" msgid="2026996642917801803">"फोन कलको लग पढ्नुहोस् र लेख्नुहोस्"</string> <string name="permgrouplab_phone" msgid="570318944091926620">"फोन"</string> @@ -437,10 +437,10 @@ <string name="permdesc_sim_communication" msgid="4179799296415957960">"SIM लाई आदेश पठाउन एपलाई अनुमति दिन्छ। यो निकै खतरनाक हुन्छ।"</string> <string name="permlab_activityRecognition" msgid="1782303296053990884">"शारीरिक गतिविधि पहिचान गर्नुहोस्"</string> <string name="permdesc_activityRecognition" msgid="8667484762991357519">"यो अनुप्रयोगले तपाईंको शारीरिक गतिविधिको पहिचान गर्न सक्छ।"</string> - <string name="permlab_camera" msgid="6320282492904119413">"तस्बिरहरू र भिडियोहरू लिनुहोस्।"</string> - <string name="permdesc_camera" msgid="1354600178048761499">"यस अनुप्रयोगले जुनसुकै समय क्यामेराको प्रयोग गरी तस्बिर खिच्न र भिडियो रेकर्ड गर्न सक्छ।"</string> - <string name="permlab_systemCamera" msgid="3642917457796210580">"एप वा सेवालाई तस्बिर र भिडियो खिच्न प्रणालीका क्यामेराहरूमाथि पहुँच राख्न दिनुहोस्"</string> - <string name="permdesc_systemCamera" msgid="5938360914419175986">"प्रणालीको यस विशेषाधिकार प्राप्त अनुप्रयोगले जुनसुकै बेला प्रणालीको क्यामेरा प्रयोग गरी तस्बिर खिच्न र भिडियो रेकर्ड गर्न सक्छ। अनुप्रयोगसँग पनि android.permission.CAMERA प्रयोग गर्ने अनुमति हुनु पर्छ"</string> + <string name="permlab_camera" msgid="6320282492904119413">"फोटोहरू र भिडियोहरू लिनुहोस्।"</string> + <string name="permdesc_camera" msgid="1354600178048761499">"यस अनुप्रयोगले जुनसुकै समय क्यामेराको प्रयोग गरी फोटो खिच्न र भिडियो रेकर्ड गर्न सक्छ।"</string> + <string name="permlab_systemCamera" msgid="3642917457796210580">"एप वा सेवालाई फोटो र भिडियो खिच्न प्रणालीका क्यामेराहरूमाथि पहुँच राख्न दिनुहोस्"</string> + <string name="permdesc_systemCamera" msgid="5938360914419175986">"प्रणालीको यस विशेषाधिकार प्राप्त अनुप्रयोगले जुनसुकै बेला प्रणालीको क्यामेरा प्रयोग गरी फोटो खिच्न र भिडियो रेकर्ड गर्न सक्छ। अनुप्रयोगसँग पनि android.permission.CAMERA प्रयोग गर्ने अनुमति हुनु पर्छ"</string> <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"कुनै एप वा सेवालाई खोलिँदै वा बन्द गरिँदै गरेका क्यामेरा यन्त्रहरूका बारेमा कलब्याक प्राप्त गर्ने अनुमति दिनुहोस्।"</string> <string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"कुनै क्यामेरा यन्त्र खोलिँदा (कुन अनुप्रयोगले खोलेको भन्ने बारेमा) वा बन्द गरिँदा यो अनुप्रयोगले कलब्याक प्राप्त गर्न सक्छ।"</string> <string name="permlab_vibrate" msgid="8596800035791962017">"कम्पन नियन्त्रण गर्नुहोस्"</string> @@ -1347,7 +1347,7 @@ <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> ले काम गरिरहेको छैन"</string> <string name="ext_media_new_notification_message" msgid="6095403121990786986">"सेटअप गर्न ट्याप गर्नुहोस्"</string> <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"तपाईंले यो यन्त्र पुनः फर्म्याट गर्नु पर्ने हुन सक्छ। यो यन्त्र हटाउन ट्याप गर्नुहोस्।"</string> - <string name="ext_media_ready_notification_message" msgid="777258143284919261">"तस्बिरहरू र मिडिया स्थानान्तरणका लागि"</string> + <string name="ext_media_ready_notification_message" msgid="777258143284919261">"फोटोहरू र मिडिया स्थानान्तरणका लागि"</string> <string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"<xliff:g id="NAME">%s</xliff:g> मा समस्या देखियो"</string> <string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> ले काम गरिरहेको छैन"</string> <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"समस्या समाधान गर्न ट्याप गर्नुहोस्"</string> @@ -1914,7 +1914,7 @@ <string name="app_category_game" msgid="4534216074910244790">"खेलहरू"</string> <string name="app_category_audio" msgid="8296029904794676222">"सङ्गीत तथा अडियो"</string> <string name="app_category_video" msgid="2590183854839565814">"चलचित्र तथा भिडियो"</string> - <string name="app_category_image" msgid="7307840291864213007">"तस्बिर तथा छविहरू"</string> + <string name="app_category_image" msgid="7307840291864213007">"फोटो तथा छविहरू"</string> <string name="app_category_social" msgid="2278269325488344054">"सामाजिक तथा सञ्चार"</string> <string name="app_category_news" msgid="1172762719574964544">"समाचार तथा पत्रिकाहरू"</string> <string name="app_category_maps" msgid="6395725487922533156">"नक्सा तथा नेभिगेसन"</string> diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java new file mode 100644 index 000000000000..7480a200ee4e --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -0,0 +1,130 @@ +/* + * 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.internal.jank; + +import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; + +import android.os.Handler; +import android.view.FrameMetrics; +import android.view.View; +import android.view.ViewAttachTestActivity; + +import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; + +import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; +import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; +import com.android.internal.jank.InteractionJankMonitor.Session; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.concurrent.TimeUnit; + +@SmallTest +public class FrameTrackerTest { + private ViewAttachTestActivity mActivity; + + @Rule + public ActivityTestRule<ViewAttachTestActivity> mRule = + new ActivityTestRule<>(ViewAttachTestActivity.class); + + private FrameTracker mTracker; + private ThreadedRendererWrapper mRenderer; + private FrameMetricsWrapper mWrapper; + + @Before + public void setup() { + // Prepare an activity for getting ThreadedRenderer later. + mActivity = mRule.getActivity(); + View view = mActivity.getWindow().getDecorView(); + assertThat(view.isAttachedToWindow()).isTrue(); + + Handler handler = mRule.getActivity().getMainThreadHandler(); + mWrapper = Mockito.spy(new FrameMetricsWrapper()); + mRenderer = Mockito.spy(new ThreadedRendererWrapper(view.getThreadedRenderer())); + doNothing().when(mRenderer).addObserver(any()); + doNothing().when(mRenderer).removeObserver(any()); + + Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE); + mTracker = Mockito.spy(new FrameTracker(session, handler, mRenderer, mWrapper)); + doNothing().when(mTracker).triggerPerfetto(); + } + + @Test + public void testIsJankyFrame() { + // We skip the first frame. + doReturn(1L).when(mWrapper).getMetric(FrameMetrics.FIRST_DRAW_FRAME); + doReturn(TimeUnit.MILLISECONDS.toNanos(20L)) + .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION); + assertThat(mTracker.isJankyFrame(mWrapper)).isFalse(); + + // Should exceed the criteria. + doReturn(0L).when(mWrapper).getMetric(FrameMetrics.FIRST_DRAW_FRAME); + doReturn(TimeUnit.MILLISECONDS.toNanos(20L)) + .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION); + assertThat(mTracker.isJankyFrame(mWrapper)).isTrue(); + + // Should be safe. + doReturn(TimeUnit.MILLISECONDS.toNanos(10L)) + .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION); + assertThat(mTracker.isJankyFrame(mWrapper)).isFalse(); + } + + @Test + public void testBeginEnd() { + // assert the initial values + assertThat(mTracker.mBeginTime).isEqualTo(FrameTracker.UNKNOWN_TIMESTAMP); + assertThat(mTracker.mEndTime).isEqualTo(FrameTracker.UNKNOWN_TIMESTAMP); + + // Observer should be only added once in continuous calls. + mTracker.begin(); + mTracker.begin(); + verify(mRenderer, only()).addObserver(any()); + + // assert the values after begin call. + assertThat(mTracker.mBeginTime).isNotEqualTo(FrameTracker.UNKNOWN_TIMESTAMP); + assertThat(mTracker.mEndTime).isEqualTo(FrameTracker.UNKNOWN_TIMESTAMP); + + // simulate the callback during trace session + // assert the isJankyFrame should be invoked as well. + doReturn(System.nanoTime()).when(mWrapper).getMetric(FrameMetrics.VSYNC_TIMESTAMP); + doReturn(true).when(mTracker).isJankyFrame(any()); + mTracker.onFrameMetricsAvailable(0); + verify(mTracker).isJankyFrame(any()); + + // end the trace session, simulate a callback came after the end call. + // assert the end time should be set, the observer should be removed. + // triggerPerfetto should be invoked as well. + mTracker.end(); + doReturn(System.nanoTime()).when(mWrapper).getMetric(FrameMetrics.VSYNC_TIMESTAMP); + assertThat(mTracker.mEndTime).isNotEqualTo(FrameTracker.UNKNOWN_TIMESTAMP); + mTracker.onFrameMetricsAvailable(0); + verify(mRenderer).removeObserver(any()); + verify(mTracker).triggerPerfetto(); + } +} diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java new file mode 100644 index 000000000000..5c0b0c94f6d0 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -0,0 +1,120 @@ +/* + * 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.internal.jank; + +import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; + +import android.os.Handler; +import android.os.HandlerThread; +import android.view.View; +import android.view.ViewAttachTestActivity; + +import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; + +import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; +import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper; +import com.android.internal.jank.InteractionJankMonitor.Session; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mockito; +import org.testng.Assert; + +import java.util.HashMap; +import java.util.Map; + +@SmallTest +public class InteractionJankMonitorTest { + private ViewAttachTestActivity mActivity; + private View mView; + private FrameTracker mTracker; + + @Rule + public ActivityTestRule<ViewAttachTestActivity> mRule = + new ActivityTestRule<>(ViewAttachTestActivity.class); + + @Before + public void setup() { + // Prepare an activity for getting ThreadedRenderer later. + mActivity = mRule.getActivity(); + mView = mActivity.getWindow().getDecorView(); + assertThat(mView.isAttachedToWindow()).isTrue(); + + InteractionJankMonitor.reset(); + + // Prepare a FrameTracker to inject. + Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE); + FrameMetricsWrapper wrapper = Mockito.spy(new FrameTracker.FrameMetricsWrapper()); + ThreadedRendererWrapper renderer = + Mockito.spy(new ThreadedRendererWrapper(mView.getThreadedRenderer())); + Handler handler = mActivity.getMainThreadHandler(); + mTracker = Mockito.spy(new FrameTracker(session, handler, renderer, wrapper)); + } + + @Test + public void testBeginEnd() { + // Should throw exception if the view is not attached. + Assert.assertThrows(IllegalStateException.class, + () -> InteractionJankMonitor.init(new View(mActivity))); + + // Verify we init InteractionJankMonitor correctly. + Map<String, FrameTracker> map = new HashMap<>(); + HandlerThread worker = Mockito.spy(new HandlerThread("Aot-test")); + doNothing().when(worker).start(); + InteractionJankMonitor.init(mView, mView.getThreadedRenderer(), map, worker); + verify(worker).start(); + + // Simulate a trace session and see if begin / end are invoked. + Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE); + assertThat(map.get(session.getName())).isNull(); + InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE, mTracker); + verify(mTracker).begin(); + assertThat(map.get(session.getName())).isEqualTo(mTracker); + InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE); + verify(mTracker).end(); + assertThat(map.get(session.getName())).isNull(); + } + + @Test + public void testCheckInitState() { + // Should throw exception if invoking begin / end without init invocation. + Assert.assertThrows(IllegalStateException.class, + () -> InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE)); + Assert.assertThrows(IllegalStateException.class, + () -> InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE)); + + // Everything should be fine if invoking init first. + boolean thrown = false; + try { + InteractionJankMonitor.init(mActivity.getWindow().getDecorView()); + InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE); + InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE); + } catch (Exception ex) { + thrown = true; + } finally { + assertThat(thrown).isFalse(); + } + } + +} diff --git a/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java b/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java index 63a68e99b788..ab13fd7d81e0 100644 --- a/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java +++ b/core/tests/coretests/src/com/android/internal/view/ScrollViewCaptureHelperTest.java @@ -21,12 +21,11 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; -import static androidx.test.InstrumentationRegistry.getContext; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.content.Context; import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.Rect; @@ -40,10 +39,12 @@ import android.widget.ScrollView; import android.widget.TextView; import androidx.test.annotation.UiThreadTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.internal.view.ScrollCaptureViewHelper.ScrollResult; import org.junit.After; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; import java.util.Random; @@ -67,28 +68,27 @@ public class ScrollViewCaptureHelperTest { private Random mRandom; - private static float sDensity; - - @BeforeClass - public static void setUpClass() { - sDensity = getContext().getResources().getDisplayMetrics().density; - } + private Context mContext; + private float mDensity; @Before @UiThreadTest public void setUp() { + mContext = InstrumentationRegistry.getInstrumentation().getContext(); + mDensity = mContext.getResources().getDisplayMetrics().density; + mRandom = new Random(); - mParent = new FrameLayout(getContext()); + mParent = new FrameLayout(mContext); - mTarget = new ScrollView(getContext()); + mTarget = new ScrollView(mContext); mParent.addView(mTarget, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); - mContent = new LinearLayout(getContext()); + mContent = new LinearLayout(mContext); mContent.setOrientation(LinearLayout.VERTICAL); mTarget.addView(mContent, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); for (int i = 0; i < CHILD_VIEWS; i++) { - TextView view = new TextView(getContext()); + TextView view = new TextView(mContext); view.setText("Child #" + i); view.setTextColor(Color.WHITE); view.setTextSize(30f); @@ -99,7 +99,7 @@ public class ScrollViewCaptureHelperTest { // Window -> Parent -> Target -> Content - mWm = getContext().getSystemService(WindowManager.class); + mWm = mContext.getSystemService(WindowManager.class); // Setup the window that we are going to use mWindowLayoutParams = new WindowManager.LayoutParams(WINDOW_WIDTH, WINDOW_HEIGHT, @@ -123,58 +123,6 @@ public class ScrollViewCaptureHelperTest { svc.onPrepareForStart(mTarget, scrollBounds); } - static void assertEmpty(Rect r) { - if (r != null && !r.isEmpty()) { - fail("Not true that " + r + " is empty"); - } - } - - static void assertContains(Rect parent, Rect child) { - if (!parent.contains(child)) { - fail("Not true that " + parent + " contains " + child); - } - } - - static void assertRectEquals(Rect parent, Rect child) { - if (!parent.equals(child)) { - fail("Not true that " + parent + " is equal to " + child); - } - } - - static Rect getVisibleRect(View v) { - Rect r = new Rect(0, 0, v.getWidth(), v.getHeight()); - v.getLocalVisibleRect(r); - return r; - } - - - static int assertScrollToY(View v, int scrollY) { - v.scrollTo(0, scrollY); - int dest = v.getScrollY(); - assertEquals(scrollY, dest); - return scrollY; - } - - - static void assertCapturedAreaCompletelyVisible(int startScrollY, Rect requestRect, - Rect localVisibleNow) { - Rect captured = new Rect(localVisibleNow); - captured.offset(0, -startScrollY); // make relative - - if (!captured.contains(requestRect)) { - fail("Not true that all of " + requestRect + " is contained by " + captured); - } - } - static void assertCapturedAreaPartiallyVisible(int startScrollY, Rect requestRect, - Rect localVisibleNow) { - Rect captured = new Rect(localVisibleNow); - captured.offset(0, -startScrollY); // make relative - - if (!Rect.intersects(captured, requestRect)) { - fail("Not true that any of " + requestRect + " intersects " + captured); - } - } - @Test @UiThreadTest public void onScrollRequested_up_fromTop() { @@ -188,12 +136,13 @@ public class ScrollViewCaptureHelperTest { Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0); - Rect result = svc.onScrollRequested(mTarget, scrollBounds, request); + ScrollResult scrollResult = svc.onScrollRequested(mTarget, + scrollBounds, request); // The result is an empty rectangle and no scrolling, since it // is not possible to physically scroll further up to make the // requested area visible at all (it doesn't exist). - assertEmpty(result); + assertEmpty(scrollResult.availableArea); } @Test @@ -201,7 +150,6 @@ public class ScrollViewCaptureHelperTest { public void onScrollRequested_down_fromTop() { final int startScrollY = assertScrollToY(mTarget, 0); - ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper(); Rect scrollBounds = svc.onComputeScrollBounds(mTarget); svc.onPrepareForStart(mTarget, scrollBounds); @@ -212,13 +160,13 @@ public class ScrollViewCaptureHelperTest { Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(), WINDOW_HEIGHT + CAPTURE_HEIGHT); - Rect result = svc.onScrollRequested(mTarget, scrollBounds, request); - assertRectEquals(request, result); - - assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent)); + ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request); + assertRectEquals(request, scrollResult.requestedArea); + assertRectEquals(request, scrollResult.availableArea); + assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent)); + assertEquals(CAPTURE_HEIGHT, scrollResult.scrollDelta); } - @Test @UiThreadTest public void onScrollRequested_up_fromMiddle() { @@ -230,12 +178,11 @@ public class ScrollViewCaptureHelperTest { Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0); - - Rect result = svc.onScrollRequested(mTarget, scrollBounds, request); - - assertRectEquals(request, result); - - assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent)); + ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request); + assertRectEquals(request, scrollResult.requestedArea); + assertRectEquals(request, scrollResult.availableArea); + assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent)); + assertEquals(-CAPTURE_HEIGHT, scrollResult.scrollDelta); } @Test @@ -250,10 +197,12 @@ public class ScrollViewCaptureHelperTest { Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(), WINDOW_HEIGHT + CAPTURE_HEIGHT); - Rect result = svc.onScrollRequested(mTarget, scrollBounds, request); - assertRectEquals(request, result); + ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request); + assertRectEquals(request, scrollResult.requestedArea); + assertRectEquals(request, scrollResult.availableArea); + assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent)); + assertEquals(CAPTURE_HEIGHT, scrollResult.scrollDelta); - assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent)); } @Test @@ -267,10 +216,11 @@ public class ScrollViewCaptureHelperTest { Rect request = new Rect(0, -CAPTURE_HEIGHT, scrollBounds.width(), 0); - Rect result = svc.onScrollRequested(mTarget, scrollBounds, request); - assertRectEquals(request, result); - - assertCapturedAreaCompletelyVisible(startScrollY, request, getVisibleRect(mContent)); + ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request); + assertRectEquals(request, scrollResult.requestedArea); + assertRectEquals(request, scrollResult.availableArea); + assertRequestedRectCompletelyVisible(startScrollY, request, getVisibleRect(mContent)); + assertEquals(-CAPTURE_HEIGHT, scrollResult.scrollDelta); } @Test @@ -285,12 +235,14 @@ public class ScrollViewCaptureHelperTest { Rect request = new Rect(0, WINDOW_HEIGHT, scrollBounds.width(), WINDOW_HEIGHT + CAPTURE_HEIGHT); - Rect result = svc.onScrollRequested(mTarget, scrollBounds, request); + ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request); + assertRectEquals(request, scrollResult.requestedArea); // The result is an empty rectangle and no scrolling, since it // is not possible to physically scroll further down to make the // requested area visible at all (it doesn't exist). - assertEmpty(result); + assertEmpty(scrollResult.availableArea); + assertEquals(0, scrollResult.scrollDelta); } @Test @@ -309,12 +261,16 @@ public class ScrollViewCaptureHelperTest { 0, top - (CAPTURE_HEIGHT / 2), scrollBounds.width(), top + (CAPTURE_HEIGHT / 2)); - Rect result = svc.onScrollRequested(mTarget, scrollBounds, request); + ScrollResult scrollResult = svc.onScrollRequested(mTarget, scrollBounds, request); + assertRectEquals(request, scrollResult.requestedArea); + + ScrollResult result = svc.onScrollRequested(mTarget, scrollBounds, request); // The result is a partial result Rect expectedResult = new Rect(request); expectedResult.top += 300; // top half clipped - assertRectEquals(expectedResult, result); - assertCapturedAreaPartiallyVisible(startScrollY, request, getVisibleRect(mContent)); + assertRectEquals(expectedResult, result.availableArea); + assertRequestedRectPartiallyVisible(startScrollY, request, getVisibleRect(mContent)); + assertEquals(0, scrollResult.scrollDelta); } @Test @@ -334,13 +290,13 @@ public class ScrollViewCaptureHelperTest { 0, bottom - (CAPTURE_HEIGHT / 2), scrollBounds.width(), bottom + (CAPTURE_HEIGHT / 2)); - Rect result = svc.onScrollRequested(mTarget, scrollBounds, request); + ScrollResult result = svc.onScrollRequested(mTarget, scrollBounds, request); Rect expectedResult = new Rect(request); expectedResult.bottom -= 300; // bottom half clipped - assertRectEquals(expectedResult, result); - assertCapturedAreaPartiallyVisible(startScrollY, request, getVisibleRect(mContent)); - + assertRectEquals(expectedResult, result.availableArea); + assertRequestedRectPartiallyVisible(startScrollY, request, getVisibleRect(mContent)); + assertEquals(0, result.scrollDelta); } @Test @@ -349,4 +305,56 @@ public class ScrollViewCaptureHelperTest { ScrollViewCaptureHelper svc = new ScrollViewCaptureHelper(); svc.onPrepareForEnd(mTarget); } + + + static void assertEmpty(Rect r) { + if (r != null && !r.isEmpty()) { + fail("Not true that " + r + " is empty"); + } + } + + static void assertContains(Rect parent, Rect child) { + if (!parent.contains(child)) { + fail("Not true that " + parent + " contains " + child); + } + } + + static void assertRectEquals(Rect parent, Rect child) { + if (!parent.equals(child)) { + fail("Not true that " + parent + " is equal to " + child); + } + } + + static Rect getVisibleRect(View v) { + Rect r = new Rect(0, 0, v.getWidth(), v.getHeight()); + v.getLocalVisibleRect(r); + return r; + } + + + static int assertScrollToY(View v, int scrollY) { + v.scrollTo(0, scrollY); + int dest = v.getScrollY(); + assertEquals(scrollY, dest); + return scrollY; + } + + static void assertRequestedRectCompletelyVisible(int startScrollY, Rect requestRect, + Rect localVisibleNow) { + Rect captured = new Rect(localVisibleNow); + captured.offset(0, -startScrollY); // make relative + + if (!captured.contains(requestRect)) { + fail("Not true that all of " + requestRect + " is contained by " + captured); + } + } + static void assertRequestedRectPartiallyVisible(int startScrollY, Rect requestRect, + Rect localVisibleNow) { + Rect captured = new Rect(localVisibleNow); + captured.offset(0, -startScrollY); // make relative + + if (!Rect.intersects(captured, requestRect)) { + fail("Not true that any of " + requestRect + " intersects " + captured); + } + } } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 75eb7b64a444..a2a2216028ac 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -325,12 +325,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "-1587841219": { - "message": "Focus moving from %s to %s displayId=%d", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "-1568331821": { "message": "Enabling listeners", "level": "VERBOSE", @@ -415,6 +409,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1438175584": { + "message": "Input focus has changed to %s display=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/InputMonitor.java" + }, "-1434147454": { "message": "cleanupAnimation(): Notify animation finished mPendingAnimations=%d reorderMode=%d", "level": "DEBUG", @@ -565,6 +565,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, + "-1142279614": { + "message": "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "-1130891072": { "message": "Orientation continue waiting for draw in %s", "level": "VERBOSE", @@ -799,12 +805,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-771282525": { - "message": "Losing focus: %s", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "-771177730": { "message": "Removing focused app token:%s displayId=%d", "level": "VERBOSE", @@ -847,12 +847,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DragState.java" }, - "-687185281": { - "message": "New topFocusedDisplayId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/RootWindowContainer.java" - }, "-668956537": { "message": " THUMBNAIL %s: CREATE", "level": "INFO", @@ -877,6 +871,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowToken.java" }, + "-639217716": { + "message": "setFocusedApp %s displayId=%d Callers=%s", + "level": "INFO", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "-635082269": { "message": "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b wallEnabled=%b haveKeyguard=%b", "level": "INFO", @@ -895,12 +895,6 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-603199586": { - "message": "Clearing focused app, displayId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "-593535526": { "message": "Binding proc %s with config %s", "level": "VERBOSE", @@ -919,6 +913,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, + "-561092364": { + "message": "onPointerDownOutsideFocusLocked called on %s", + "level": "INFO", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "-549028919": { "message": "enableScreenIfNeededLocked: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s", "level": "INFO", @@ -1201,12 +1201,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, - "-96848838": { - "message": "Gaining focus: %s", - "level": "INFO", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "-90559682": { "message": "Config is skipping already pausing %s", "level": "VERBOSE", @@ -1363,6 +1357,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "115358443": { + "message": "Focus changing: %s -> %s", + "level": "INFO", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "123161180": { "message": "SEVER CHILDREN", "level": "INFO", @@ -1495,12 +1495,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/AppTransition.java" }, - "285317231": { - "message": "Input focus has changed to %s", - "level": "DEBUG", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/InputMonitor.java" - }, "288485303": { "message": "Attempted to set remove mode to a display that does not exist: %d", "level": "WARN", @@ -1537,6 +1531,12 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowSurfaceController.java" }, + "312030608": { + "message": "New topFocusedDisplayId=%d", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/RootWindowContainer.java" + }, "315395835": { "message": "Trying to add window with invalid user=%d", "level": "WARN", @@ -1699,12 +1699,6 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowSurfaceController.java" }, - "584499099": { - "message": "Set focused app to: %s moveFocusNow=%b displayId=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "585096182": { "message": "SURFACE isColorSpaceAgnostic=%b: %s", "level": "INFO", @@ -1747,6 +1741,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "620519522": { + "message": "findFocusedWindow: No focusable windows, display=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "628276090": { "message": "Delaying app transition for screen rotation animation to finish", "level": "VERBOSE", @@ -1903,6 +1903,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "872933199": { + "message": "Changing focus from %s to %s displayId=%d Callers=%s", + "level": "DEBUG", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "873914452": { "message": "goodToGo()", "level": "DEBUG", @@ -2137,12 +2143,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" }, - "1358462645": { - "message": "Looking for focus: %s, flags=%d, canReceive=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "1360551978": { "message": "Trying to update display configuration for non-existing displayId=%d", "level": "WARN", @@ -2233,12 +2233,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, - "1469292670": { - "message": "Changing focus from %s to %s displayId=%d Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "1495525537": { "message": "createWallpaperAnimations()", "level": "DEBUG", @@ -2677,12 +2671,6 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/DisplayContent.java" }, - "2128604122": { - "message": "findFocusedWindow: No focusable windows.", - "level": "VERBOSE", - "group": "WM_DEBUG_FOCUS_LIGHT", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "2128917433": { "message": "onProposedRotationChanged, rotation=%d", "level": "VERBOSE", diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 1c0a526f536c..de2a7b2c15e3 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -807,7 +807,8 @@ public class AudioTrack extends PlayerBase int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/, - offload, encapsulationMode, tunerConfiguration); + offload, encapsulationMode, tunerConfiguration, + getCurrentOpPackageName()); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -893,7 +894,8 @@ public class AudioTrack extends PlayerBase nativeTrackInJavaObj, false /*offload*/, ENCAPSULATION_MODE_NONE, - null /* tunerConfiguration */); + null /* tunerConfiguration */, + "" /* opPackagename */); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); return; // with mState == STATE_UNINITIALIZED @@ -4062,7 +4064,8 @@ public class AudioTrack extends PlayerBase Object /*AudioAttributes*/ attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack, - boolean offload, int encapsulationMode, Object tunerConfiguration); + boolean offload, int encapsulationMode, Object tunerConfiguration, + @NonNull String opPackageName); private native final void native_finalize(); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 49e416080041..36ae3ec75a99 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -672,7 +672,8 @@ public class MediaPlayer extends PlayerBase /* Native setup requires a weak reference to our object. * It's easier to create it here than in C++. */ - native_setup(new WeakReference<MediaPlayer>(this)); + native_setup(new WeakReference<MediaPlayer>(this), + getCurrentOpPackageName()); baseRegisterPlayer(); } @@ -2378,7 +2379,7 @@ public class MediaPlayer extends PlayerBase private native final int native_setMetadataFilter(Parcel request); private static native final void native_init(); - private native final void native_setup(Object mediaplayer_this); + private native void native_setup(Object mediaplayerThis, @NonNull String opPackageName); private native final void native_finalize(); /** diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java index ee8f1b3eec77..df5e85edbc30 100644 --- a/media/java/android/media/PlayerBase.java +++ b/media/java/android/media/PlayerBase.java @@ -27,6 +27,7 @@ import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -622,4 +623,8 @@ public abstract class PlayerBase { Log.w(className, "See the documentation of " + opName + " for what to use instead with " + "android.media.AudioAttributes to qualify your playback use case"); } + + protected String getCurrentOpPackageName() { + return TextUtils.emptyIfNull(ActivityThread.currentOpPackageName()); + } } diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 55aac09b0f65..bd8d2e9f77a4 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -33,6 +33,7 @@ #include <utils/threads.h> #include "jni.h" #include <nativehelper/JNIPlatformHelp.h> +#include <nativehelper/ScopedUtfChars.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #include "android_runtime/Log.h" @@ -944,10 +945,12 @@ android_media_MediaPlayer_native_init(JNIEnv *env) } static void -android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) +android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, + jstring opPackageName) { ALOGV("native_setup"); - sp<MediaPlayer> mp = new MediaPlayer(); + ScopedUtfChars opPackageNameStr(env, opPackageName); + sp<MediaPlayer> mp = new MediaPlayer(opPackageNameStr.c_str()); if (mp == NULL) { jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); return; @@ -1403,7 +1406,7 @@ static const JNINativeMethod gMethods[] = { {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter}, {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata}, {"native_init", "()V", (void *)android_media_MediaPlayer_native_init}, - {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup}, + {"native_setup", "(Ljava/lang/Object;Ljava/lang/String;)V",(void *)android_media_MediaPlayer_native_setup}, {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize}, {"getAudioSessionId", "()I", (void *)android_media_MediaPlayer_get_audio_session_id}, {"setAudioSessionId", "(I)V", (void *)android_media_MediaPlayer_set_audio_session_id}, diff --git a/media/packages/BluetoothMidiService/Android.bp b/media/packages/BluetoothMidiService/Android.bp index 48fc329276bd..25c34c3631dc 100644 --- a/media/packages/BluetoothMidiService/Android.bp +++ b/media/packages/BluetoothMidiService/Android.bp @@ -29,6 +29,5 @@ android_app { "src/**/*.java", ], platform_apis: true, - certificate: "platform", manifest: "AndroidManifest.xml", } diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml index b88bf2a0b2b7..fc96fd926e2d 100644 --- a/media/packages/BluetoothMidiService/AndroidManifest.xml +++ b/media/packages/BluetoothMidiService/AndroidManifest.xml @@ -19,8 +19,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.android.bluetoothmidiservice" - android:versionCode="1" - android:versionName="R-initial" > <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> diff --git a/media/packages/BluetoothMidiService/AndroidManifestBase.xml b/media/packages/BluetoothMidiService/AndroidManifestBase.xml index ebe62b039434..bfb05469adb9 100644 --- a/media/packages/BluetoothMidiService/AndroidManifestBase.xml +++ b/media/packages/BluetoothMidiService/AndroidManifestBase.xml @@ -18,8 +18,6 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.bluetoothmidiservice" - android:versionCode="1" - android:versionName="R-initial" > <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> <application diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index 3eea5132da1d..51fda965dcd0 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -52,10 +52,10 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.ShadeControllerImpl; import com.android.systemui.statusbar.policy.BatteryController; @@ -92,7 +92,7 @@ abstract class CarSystemUIModule { Context context, StatusBarStateController statusBarStateController, KeyguardBypassController bypassController, - NotificationGroupManager groupManager, + GroupMembershipManager groupManager, ConfigurationController configurationController) { return new HeadsUpManagerPhone(context, statusBarStateController, bypassController, groupManager, configurationController); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java index a2ba880facfe..fef032414bb9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedControllerImpl.java @@ -16,20 +16,19 @@ package com.android.systemui.car; +import android.annotation.NonNull; import android.app.ActivityManager; import android.car.settings.CarSettings; -import android.content.ContentResolver; -import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; -import android.provider.Settings; -import com.android.systemui.Dependency; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.settings.SecureSettings; import javax.inject.Inject; @@ -40,30 +39,33 @@ import javax.inject.Inject; @SysUISingleton public class CarDeviceProvisionedControllerImpl extends DeviceProvisionedControllerImpl implements CarDeviceProvisionedController { - private static final Uri USER_SETUP_IN_PROGRESS_URI = Settings.Secure.getUriFor( - CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS); - private final ContentObserver mCarSettingsObserver = new ContentObserver( - Dependency.get(Dependency.MAIN_HANDLER)) { - - @Override - public void onChange(boolean selfChange, Uri uri, int flags) { - if (USER_SETUP_IN_PROGRESS_URI.equals(uri)) { - notifyUserSetupInProgressChanged(); - } - } - }; - private final ContentResolver mContentResolver; + private final Uri mUserSetupInProgressUri; + private final ContentObserver mCarSettingsObserver; + private final Handler mMainHandler; + private final SecureSettings mSecureSettings; @Inject - public CarDeviceProvisionedControllerImpl(Context context, @Main Handler mainHandler, - BroadcastDispatcher broadcastDispatcher) { - super(context, mainHandler, broadcastDispatcher); - mContentResolver = context.getContentResolver(); + public CarDeviceProvisionedControllerImpl(@Main Handler mainHandler, + BroadcastDispatcher broadcastDispatcher, GlobalSettings globalSetting, + SecureSettings secureSettings) { + super(mainHandler, broadcastDispatcher, globalSetting, secureSettings); + mMainHandler = mainHandler; + mSecureSettings = secureSettings; + mUserSetupInProgressUri = mSecureSettings.getUriFor( + CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS); + mCarSettingsObserver = new ContentObserver(mMainHandler) { + @Override + public void onChange(boolean selfChange, Uri uri, int flags) { + if (mUserSetupInProgressUri.equals(uri)) { + notifyUserSetupInProgressChanged(); + } + } + }; } @Override public boolean isUserSetupInProgress(int user) { - return Settings.Secure.getIntForUser(mContentResolver, + return mSecureSettings.getIntForUser( CarSettings.Secure.KEY_SETUP_WIZARD_IN_PROGRESS, /* def= */ 0, user) != 0; } @@ -73,7 +75,7 @@ public class CarDeviceProvisionedControllerImpl extends DeviceProvisionedControl } @Override - public void addCallback(DeviceProvisionedListener listener) { + public void addCallback(@NonNull DeviceProvisionedListener listener) { super.addCallback(listener); if (listener instanceof CarDeviceProvisionedListener) { ((CarDeviceProvisionedListener) listener).onUserSetupInProgressChanged(); @@ -82,9 +84,9 @@ public class CarDeviceProvisionedControllerImpl extends DeviceProvisionedControl @Override protected void startListening(int user) { - mContentResolver.registerContentObserver( - USER_SETUP_IN_PROGRESS_URI, /* notifyForDescendants= */ true, mCarSettingsObserver, - user); + mSecureSettings.registerContentObserverForUser( + mUserSetupInProgressUri, /* notifyForDescendants= */ true, + mCarSettingsObserver, user); // The SUW Flag observer is registered before super.startListening() so that the observer is // in place before DeviceProvisionedController starts to track user switches which avoids // an edge case where our observer gets registered twice. @@ -94,16 +96,16 @@ public class CarDeviceProvisionedControllerImpl extends DeviceProvisionedControl @Override protected void stopListening() { super.stopListening(); - mContentResolver.unregisterContentObserver(mCarSettingsObserver); + mSecureSettings.unregisterContentObserver(mCarSettingsObserver); } @Override public void onUserSwitched(int newUserId) { super.onUserSwitched(newUserId); - mContentResolver.unregisterContentObserver(mCarSettingsObserver); - mContentResolver.registerContentObserver( - USER_SETUP_IN_PROGRESS_URI, /* notifyForDescendants= */ true, mCarSettingsObserver, - newUserId); + mSecureSettings.unregisterContentObserver(mCarSettingsObserver); + mSecureSettings.registerContentObserverForUser( + mUserSetupInProgressUri, /* notifyForDescendants= */ true, + mCarSettingsObserver, newUserId); } private void notifyUserSetupInProgressChanged() { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java index 276ddfbc2b4f..dadbc22760b9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java @@ -18,7 +18,6 @@ package com.android.systemui.car.keyguard; import android.car.Car; import android.car.user.CarUserManager; -import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.util.Log; @@ -28,20 +27,17 @@ import android.view.ViewRootImpl; import androidx.annotation.VisibleForTesting; -import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; -import com.android.systemui.SystemUIFactory; import com.android.systemui.car.CarServiceProvider; import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.car.window.OverlayViewController; import com.android.systemui.car.window.OverlayViewGlobalStateController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.keyguard.DismissCallbackRegistry; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -63,18 +59,14 @@ public class CarKeyguardViewController extends OverlayViewController implements private static final String TAG = "CarKeyguardViewController"; private static final boolean DEBUG = true; - private final Context mContext; private final Handler mHandler; private final CarServiceProvider mCarServiceProvider; private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; - private final LockPatternUtils mLockPatternUtils; - private final FalsingManager mFalsingManager; - private final Lazy<KeyguardBypassController> mKeyguardBypassControllerLazy; - private final DismissCallbackRegistry mDismissCallbackRegistry; private final ViewMediatorCallback mViewMediatorCallback; private final CarNavigationBarController mCarNavigationBarController; + private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; // Needed to instantiate mBouncer. private final KeyguardBouncer.BouncerExpansionCallback mExpansionCallback = new KeyguardBouncer.BouncerExpansionCallback() { @@ -107,7 +99,6 @@ public class CarKeyguardViewController extends OverlayViewController implements @Inject public CarKeyguardViewController( - Context context, @Main Handler mainHandler, CarServiceProvider carServiceProvider, OverlayViewGlobalStateController overlayViewGlobalStateController, @@ -116,26 +107,18 @@ public class CarKeyguardViewController extends OverlayViewController implements Lazy<BiometricUnlockController> biometricUnlockControllerLazy, ViewMediatorCallback viewMediatorCallback, CarNavigationBarController carNavigationBarController, - /* The params below are only used to reuse KeyguardBouncer */ - LockPatternUtils lockPatternUtils, - DismissCallbackRegistry dismissCallbackRegistry, - FalsingManager falsingManager, - Lazy<KeyguardBypassController> keyguardBypassControllerLazy) { + KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) { super(R.id.keyguard_stub, overlayViewGlobalStateController); - mContext = context; mHandler = mainHandler; mCarServiceProvider = carServiceProvider; mKeyguardStateController = keyguardStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; - mLockPatternUtils = lockPatternUtils; - mFalsingManager = falsingManager; - mKeyguardBypassControllerLazy = keyguardBypassControllerLazy; - mDismissCallbackRegistry = dismissCallbackRegistry; mViewMediatorCallback = viewMediatorCallback; mCarNavigationBarController = carNavigationBarController; + mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory; registerUserSwitchedListener(); } @@ -147,11 +130,9 @@ public class CarKeyguardViewController extends OverlayViewController implements @Override public void onFinishInflate() { - mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, - mViewMediatorCallback, mLockPatternUtils, - getLayout().findViewById(R.id.keyguard_container), mDismissCallbackRegistry, - mExpansionCallback, mKeyguardStateController, mFalsingManager, - mKeyguardBypassControllerLazy.get()); + mBouncer = mKeyguardBouncerComponentFactory + .build(getLayout().findViewById(R.id.keyguard_container), mExpansionCallback) + .createKeyguardBouncer(); mBiometricUnlockControllerLazy.get().setKeyguardViewController(this); } @@ -359,9 +340,8 @@ public class CarKeyguardViewController extends OverlayViewController implements public void registerStatusBar(StatusBar statusBar, ViewGroup container, NotificationPanelViewController notificationPanelViewController, BiometricUnlockController biometricUnlockController, - DismissCallbackRegistry dismissCallbackRegistry, ViewGroup lockIconContainer, - View notificationContainer, KeyguardBypassController bypassController, - FalsingManager falsingManager) { + ViewGroup lockIconContainer, + View notificationContainer, KeyguardBypassController bypassController) { // no-op } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index 38e1a48ab3a7..fe4cba8e73cd 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -91,7 +91,6 @@ public class NotificationPanelViewController extends OverlayPanelViewController private RecyclerView mNotificationList; private NotificationViewController mNotificationViewController; - private boolean mIsTracking; private boolean mNotificationListAtEnd; private float mFirstTouchDownOnGlassPane; private boolean mNotificationListAtEndAtTimeOfTouch; @@ -306,7 +305,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController mFirstTouchDownOnGlassPane = event.getRawX(); mNotificationListAtEndAtTimeOfTouch = mNotificationListAtEnd; // Reset the tracker when there is a touch down on the glass pane. - mIsTracking = false; + setIsTracking(false); // Pass the down event to gesture detector so that it knows where the touch event // started. closeGestureDetector.onTouchEvent(event); @@ -341,15 +340,15 @@ public class NotificationPanelViewController extends OverlayPanelViewController // If the card is swiping we should not allow the notification shade to close. // Hence setting mNotificationListAtEndAtTimeOfTouch to false will stop that - // for us. We are also checking for mIsTracking because while swiping the + // for us. We are also checking for isTracking() because while swiping the // notification shade to close if the user goes a bit horizontal while swiping // upwards then also this should close. - if (mIsNotificationCardSwiping && !mIsTracking) { + if (mIsNotificationCardSwiping && !isTracking()) { mNotificationListAtEndAtTimeOfTouch = false; } boolean handled = closeGestureDetector.onTouchEvent(event); - boolean isTracking = mIsTracking; + boolean isTracking = isTracking(); Rect rect = getLayout().getClipBounds(); float clippedHeight = 0; if (rect != null) { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java index 5bd8797c5349..023b5b4f5f30 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserGridRecyclerView.java @@ -131,7 +131,7 @@ public class UserGridRecyclerView extends RecyclerView { } private List<UserInfo> getUsersForUserGrid() { - return mUserManager.getUsers(/* excludeDying= */ true) + return mUserManager.getAliveUsers() .stream() .filter(UserInfo::supportsSwitchToByUser) .collect(Collectors.toList()); @@ -338,7 +338,7 @@ public class UserGridRecyclerView extends RecyclerView { maxSupportedUsers -= 1; } - List<UserInfo> users = mUserManager.getUsers(/* excludeDying= */ true); + List<UserInfo> users = mUserManager.getAliveUsers(); // Count all users that are managed profiles of another user. int managedProfilesCount = 0; diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java index bde31f18d8fd..1b00c6301011 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java @@ -297,14 +297,17 @@ public abstract class OverlayPanelViewController extends OverlayViewController { float from = getCurrentStartPosition(rect); if (from != to) { animate(from, to, velocity, isClosing); - return; } + + // If we swipe down the notification panel all the way to the bottom of the screen + // (i.e. from == to), then we have finished animating the panel. + return; } // We will only be here if the shade is being opened programmatically or via button when // height of the layout was not calculated. - ViewTreeObserver notificationTreeObserver = getLayout().getViewTreeObserver(); - notificationTreeObserver.addOnGlobalLayoutListener( + ViewTreeObserver panelTreeObserver = getLayout().getViewTreeObserver(); + panelTreeObserver.addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { @@ -507,6 +510,11 @@ public abstract class OverlayPanelViewController extends OverlayViewController { return mIsTracking; } + /** Sets whether the panel is currently tracking or not. */ + protected final void setIsTracking(boolean isTracking) { + mIsTracking = isTracking; + } + /** Returns {@code true} if the panel is currently animating. */ protected final boolean isAnimating() { return mIsAnimating; @@ -545,7 +553,7 @@ public abstract class OverlayPanelViewController extends OverlayViewController { } setPanelVisible(true); - // clips the view for the notification shade when the user scrolls to open. + // clips the view for the panel when the user scrolls to open. setViewClipBounds((int) event2.getRawY()); // Initially the scroll starts with height being zero. This checks protects from divide @@ -600,11 +608,11 @@ public abstract class OverlayPanelViewController extends OverlayViewController { boolean isInClosingDirection = mAnimateDirection * distanceY > 0; // This check is to figure out if onScroll was called while swiping the card at - // bottom of the list. At that time we should not allow notification shade to + // bottom of the panel. At that time we should not allow panel to // close. We are also checking for the upwards swipe gesture here because it is - // possible if a user is closing the notification shade and while swiping starts + // possible if a user is closing the panel and while swiping starts // to open again but does not fling. At that time we should allow the - // notification shade to close fully or else it would stuck in between. + // panel to close fully or else it would stuck in between. if (Math.abs(getLayout().getHeight() - y) > SWIPE_DOWN_MIN_DISTANCE && isInClosingDirection) { setViewClipBounds((int) y); diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java index 62dc23624520..63d4004fb640 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/keyguard/CarKeyguardViewControllerTest.java @@ -26,7 +26,6 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.Context; import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -35,20 +34,17 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; -import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarServiceProvider; import com.android.systemui.car.CarSystemUiTest; import com.android.systemui.car.navigationbar.CarNavigationBarController; import com.android.systemui.car.window.OverlayViewGlobalStateController; -import com.android.systemui.keyguard.DismissCallbackRegistry; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBouncer; -import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -58,31 +54,36 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import dagger.Lazy; - @CarSystemUiTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest public class CarKeyguardViewControllerTest extends SysuiTestCase { - private TestableCarKeyguardViewController mCarKeyguardViewController; + private CarKeyguardViewController mCarKeyguardViewController; @Mock private OverlayViewGlobalStateController mOverlayViewGlobalStateController; @Mock - private KeyguardBouncer mBouncer; + private CarKeyguardViewController.OnKeyguardCancelClickedListener mCancelClickedListener; @Mock - private CarNavigationBarController mCarNavigationBarController; + private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; @Mock - private CarKeyguardViewController.OnKeyguardCancelClickedListener mCancelClickedListener; + private KeyguardBouncerComponent mKeyguardBouncerComponent; + @Mock + private KeyguardBouncer mBouncer; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mCarKeyguardViewController = new TestableCarKeyguardViewController( - mContext, + when(mKeyguardBouncerComponentFactory.build( + any(ViewGroup.class), + any(KeyguardBouncer.BouncerExpansionCallback.class))) + .thenReturn(mKeyguardBouncerComponent); + when(mKeyguardBouncerComponent.createKeyguardBouncer()).thenReturn(mBouncer); + + mCarKeyguardViewController = new CarKeyguardViewController( Handler.getMain(), mock(CarServiceProvider.class), mOverlayViewGlobalStateController, @@ -91,10 +92,7 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase { () -> mock(BiometricUnlockController.class), mock(ViewMediatorCallback.class), mock(CarNavigationBarController.class), - mock(LockPatternUtils.class), - mock(DismissCallbackRegistry.class), - mock(FalsingManager.class), - () -> mock(KeyguardBypassController.class) + mKeyguardBouncerComponentFactory ); mCarKeyguardViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate( R.layout.sysui_overlay_window, /* root= */ null)); @@ -202,33 +200,4 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase { verify(mBouncer).hide(/* destroyView= */ true); } - - private class TestableCarKeyguardViewController extends CarKeyguardViewController { - - TestableCarKeyguardViewController(Context context, - Handler mainHandler, - CarServiceProvider carServiceProvider, - OverlayViewGlobalStateController overlayViewGlobalStateController, - KeyguardStateController keyguardStateController, - KeyguardUpdateMonitor keyguardUpdateMonitor, - Lazy<BiometricUnlockController> biometricUnlockControllerLazy, - ViewMediatorCallback viewMediatorCallback, - CarNavigationBarController carNavigationBarController, - LockPatternUtils lockPatternUtils, - DismissCallbackRegistry dismissCallbackRegistry, - FalsingManager falsingManager, - Lazy<KeyguardBypassController> keyguardBypassControllerLazy) { - super(context, mainHandler, carServiceProvider, overlayViewGlobalStateController, - keyguardStateController, keyguardUpdateMonitor, biometricUnlockControllerLazy, - viewMediatorCallback, carNavigationBarController, lockPatternUtils, - dismissCallbackRegistry, falsingManager, keyguardBypassControllerLazy); - } - - @Override - public void onFinishInflate() { - super.onFinishInflate(); - setKeyguardBouncer(CarKeyguardViewControllerTest.this.mBouncer); - } - } - } diff --git a/packages/CompanionDeviceManager/TEST_MAPPING b/packages/CompanionDeviceManager/TEST_MAPPING new file mode 100644 index 000000000000..63f54fa35158 --- /dev/null +++ b/packages/CompanionDeviceManager/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "CtsOsTestCases", + "options": [ + { + "include-filter": "android.os.cts.CompanionDeviceManagerTest" + } + ] + } + ] +} diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index bc58bfc97718..c57d4ad962bd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -13,6 +13,7 @@ package com.android.settingslib.wifi; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import android.content.Context; import android.content.Intent; @@ -250,6 +251,10 @@ public class WifiStatusTracker { statusLabel = mContext.getString(R.string.wifi_status_no_internet); } return; + } else if (!isDefaultNetwork && mDefaultNetworkCapabilities != null + && mDefaultNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + statusLabel = mContext.getString(R.string.wifi_connected_low_quality); + return; } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 807fbed7d8fd..9c92b464dfbb 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2576,7 +2576,7 @@ public class SettingsProvider extends ContentProvider { public void syncSsaidTableOnStart() { synchronized (mLock) { // Verify that each user's packages and ssaid's are in sync. - for (UserInfo user : mUserManager.getUsers(true)) { + for (UserInfo user : mUserManager.getAliveUsers()) { // Get all uids for the user's packages. final List<PackageInfo> packages; try { @@ -3007,7 +3007,7 @@ public class SettingsProvider extends ContentProvider { final long identity = Binder.clearCallingIdentity(); try { - List<UserInfo> users = mUserManager.getUsers(true); + List<UserInfo> users = mUserManager.getAliveUsers(); final int userCount = users.size(); for (int i = 0; i < userCount; i++) { @@ -3244,7 +3244,7 @@ public class SettingsProvider extends ContentProvider { // is a singleton generation entry for the global settings which // is already incremented be the caller. final Uri uri = getNotificationUriFor(key, name); - final List<UserInfo> users = mUserManager.getUsers(/*excludeDying*/ true); + final List<UserInfo> users = mUserManager.getAliveUsers(); for (int i = 0; i < users.size(); i++) { final int userId = users.get(i).id; if (mUserManager.isUserRunning(UserHandle.of(userId))) { @@ -3255,7 +3255,7 @@ public class SettingsProvider extends ContentProvider { } private void notifyLocationChangeForRunningUsers() { - final List<UserInfo> users = mUserManager.getUsers(/*excludeDying=*/ true); + final List<UserInfo> users = mUserManager.getAliveUsers(); for (int i = 0; i < users.size(); i++) { final int userId = users.get(i).id; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index b0483339d14e..05172279c4ed 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -796,16 +796,6 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe securityMode != SecurityMode.None && newView.needsInput()); } - private KeyguardSecurityViewFlipper getFlipper() { - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof KeyguardSecurityViewFlipper) { - return (KeyguardSecurityViewFlipper) child; - } - } - return null; - } - private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { public void userActivity() { if (mSecurityCallback != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java index 6a90d00c1e75..9766ee128f7c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java @@ -21,9 +21,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; -import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; @@ -180,22 +178,18 @@ public interface KeyguardViewController { /** * Registers the StatusBar to which this Keyguard View is mounted. - * * @param statusBar * @param container * @param notificationPanelViewController * @param biometricUnlockController - * @param dismissCallbackRegistry * @param lockIconContainer * @param notificationContainer * @param bypassController - * @param falsingManager */ void registerStatusBar(StatusBar statusBar, ViewGroup container, NotificationPanelViewController notificationPanelViewController, BiometricUnlockController biometricUnlockController, - DismissCallbackRegistry dismissCallbackRegistry, ViewGroup lockIconContainer, View notificationContainer, - KeyguardBypassController bypassController, FalsingManager falsingManager); + KeyguardBypassController bypassController); } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java b/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java new file mode 100644 index 000000000000..e65f19db5ac2 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/dagger/ContainerView.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface ContainerView { +} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java new file mode 100644 index 000000000000..84deaca096aa --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java @@ -0,0 +1,42 @@ +/* + * 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.keyguard.dagger; + +import android.view.ViewGroup; + +import com.android.systemui.statusbar.phone.KeyguardBouncer; + +import dagger.BindsInstance; +import dagger.Subcomponent; + +/** + * Dagger Subcomponent for the {@link KeyguardBouncer}. + */ +@Subcomponent +@KeyguardBouncerScope +public interface KeyguardBouncerComponent { + /** Simple factory for {@link KeyguardBouncerComponent}. */ + @Subcomponent.Factory + interface Factory { + KeyguardBouncerComponent build( + @BindsInstance @ContainerView ViewGroup container, + @BindsInstance KeyguardBouncer.BouncerExpansionCallback bouncerExpansionCallback); + } + + /** */ + KeyguardBouncer createKeyguardBouncer(); +} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java new file mode 100644 index 000000000000..207ac2852f2f --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java @@ -0,0 +1,32 @@ +/* + * 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.keyguard.dagger; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Scope; + +/** + * Scope annotation for singleton items within the StatusBarComponent. + */ +@Documented +@Retention(RUNTIME) +@Scope +public @interface KeyguardBouncerScope {} diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 27809b50d746..ed78c94d45f9 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -77,6 +77,7 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment; import com.android.systemui.statusbar.notification.NotificationFilter; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; @@ -88,7 +89,6 @@ import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -276,7 +276,7 @@ public class Dependency { @Inject Lazy<StatusBarStateController> mStatusBarStateController; @Inject Lazy<NotificationLockscreenUserManager> mNotificationLockscreenUserManager; @Inject Lazy<NotificationGroupAlertTransferHelper> mNotificationGroupAlertTransferHelper; - @Inject Lazy<NotificationGroupManager> mNotificationGroupManager; + @Inject Lazy<NotificationGroupManagerLegacy> mNotificationGroupManager; @Inject Lazy<VisualStabilityManager> mVisualStabilityManager; @Inject Lazy<NotificationGutsManager> mNotificationGutsManager; @Inject Lazy<NotificationMediaManager> mNotificationMediaManager; @@ -468,7 +468,7 @@ public class Dependency { mProviders.put(NotificationLockscreenUserManager.class, mNotificationLockscreenUserManager::get); mProviders.put(VisualStabilityManager.class, mVisualStabilityManager::get); - mProviders.put(NotificationGroupManager.class, mNotificationGroupManager::get); + mProviders.put(NotificationGroupManagerLegacy.class, mNotificationGroupManager::get); mProviders.put(NotificationGroupAlertTransferHelper.class, mNotificationGroupAlertTransferHelper::get); mProviders.put(NotificationMediaManager.class, mNotificationMediaManager::get); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index f5c364947a2f..f15949977754 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -19,25 +19,16 @@ package com.android.systemui; import android.content.Context; import android.content.res.Resources; import android.os.Handler; -import android.os.Looper; import android.util.Log; -import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.dagger.DaggerGlobalRootComponent; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.WMComponent; -import com.android.systemui.keyguard.DismissCallbackRegistry; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; -import com.android.systemui.statusbar.phone.KeyguardBouncer; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.policy.KeyguardStateController; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; /** @@ -83,11 +74,16 @@ public class SystemUIFactory { public SystemUIFactory() {} - private void init(Context context) { + private void init(Context context) throws ExecutionException, InterruptedException { mRootComponent = buildGlobalRootComponent(context); + // Stand up WMComponent mWMComponent = mRootComponent.getWMComponentBuilder().build(); - // TODO: use WMComponent to pass APIs into the SysUIComponent. - mSysUIComponent = mRootComponent.getSysUIComponent().build(); + + // And finally, retrieve whatever SysUI needs from WMShell and build SysUI. + // TODO: StubAPIClass is just a placeholder. + mSysUIComponent = mRootComponent.getSysUIComponent() + .setStubAPIClass(mWMComponent.createStubAPIClass()) + .build(); // Every other part of our codebase currently relies on Dependency, so we // really need to ensure the Dependency gets initialized early on. @@ -101,10 +97,15 @@ public class SystemUIFactory { .build(); } + public GlobalRootComponent getRootComponent() { return mRootComponent; } + public WMComponent getWMComponent() { + return mWMComponent; + } + public SysUIComponent getSysUIComponent() { return mSysUIComponent; } @@ -129,17 +130,4 @@ public class SystemUIFactory { Handler uiHandler) { return new ScreenshotNotificationSmartActionsProvider(); } - - public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback, - LockPatternUtils lockPatternUtils, ViewGroup container, - DismissCallbackRegistry dismissCallbackRegistry, - KeyguardBouncer.BouncerExpansionCallback expansionCallback, - KeyguardStateController keyguardStateController, FalsingManager falsingManager, - KeyguardBypassController bypassController) { - return new KeyguardBouncer(context, callback, lockPatternUtils, container, - dismissCallbackRegistry, falsingManager, - expansionCallback, keyguardStateController, - Dependency.get(KeyguardUpdateMonitor.class), bypassController, - new Handler(Looper.getMainLooper())); - } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 9e9d85a7cd1c..c81b7cefbbd7 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -104,11 +104,11 @@ import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -164,7 +164,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi private final BubbleTaskStackListener mTaskStackListener; private BubbleExpandListener mExpandListener; @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer; - private final NotificationGroupManager mNotificationGroupManager; + private final NotificationGroupManagerLegacy mNotificationGroupManager; private final ShadeController mShadeController; private final FloatingContentCoordinator mFloatingContentCoordinator; private final BubbleDataRepository mDataRepository; @@ -355,7 +355,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, NotifPipeline notifPipeline, FeatureFlags featureFlags, @@ -588,11 +588,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } }); - mNotificationGroupManager.addOnGroupChangeListener( - new NotificationGroupManager.OnGroupChangeListener() { + mNotificationGroupManager.registerGroupChangeListener( + new NotificationGroupManagerLegacy.OnGroupChangeListener() { @Override public void onGroupSuppressionChanged( - NotificationGroupManager.NotificationGroup group, + NotificationGroupManagerLegacy.NotificationGroup group, boolean suppressed) { // More notifications could be added causing summary to no longer // be suppressed -- in this case need to remove the key. @@ -650,8 +650,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // 3. User removes all bubbles // 4. We expect all the removed bubbles AND the summary (note: the summary was // never added to the suppressedSummary list in BubbleData, so we add this check) - NotificationEntry summary = - mNotificationGroupManager.getLogicalGroupSummary(entry.getSbn()); + NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(entry); if (summary != null) { ArrayList<NotificationEntry> summaryChildren = mNotificationGroupManager.getLogicalChildren(summary.getSbn()); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java index eecc41c697b3..9efc3c20f55a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java @@ -34,8 +34,8 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -62,7 +62,7 @@ public interface BubbleModule { NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager notifUserManager, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, NotifPipeline notifPipeline, FeatureFlags featureFlags, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index fd4a4093110f..c5dc8cccfdf4 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -16,6 +16,8 @@ package com.android.systemui.dagger; +import com.android.systemui.util.concurrency.GlobalConcurrencyModule; + import dagger.Module; /** @@ -33,6 +35,8 @@ import dagger.Module; * * Please use discretion when adding things to the global scope. */ -@Module(includes = {FrameworkServicesModule.class}) +@Module(includes = { + FrameworkServicesModule.class, + GlobalConcurrencyModule.class}) public class GlobalModule { } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java index 36fd3373290d..00fdf55b28e0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java @@ -18,6 +18,8 @@ package com.android.systemui.dagger; import android.content.Context; +import com.android.systemui.util.concurrency.ThreadFactory; + import javax.inject.Singleton; import dagger.BindsInstance; @@ -53,4 +55,9 @@ public interface GlobalRootComponent { * Builder for a SysuiComponent. */ SysUIComponent.Builder getSysUIComponent(); + + /** + * Build a {@link ThreadFactory}. + */ + ThreadFactory createThreadFactory(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index a8ed043c4361..2622593880ba 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -26,6 +26,7 @@ import com.android.systemui.pip.phone.dagger.PipModule; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; +import dagger.BindsInstance; import dagger.Subcomponent; /** @@ -46,6 +47,9 @@ public interface SysUIComponent { */ @Subcomponent.Builder interface Builder { + @BindsInstance + Builder setStubAPIClass(WMComponent.StubAPIClass stubAPIClass); + SysUIComponent build(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index a021114c138b..2c0b04fed810 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -46,11 +46,11 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.ShadeControllerImpl; @@ -143,7 +143,7 @@ public abstract class SystemUIDefaultModule { Context context, StatusBarStateController statusBarStateController, KeyguardBypassController bypassController, - NotificationGroupManager groupManager, + GroupMembershipManager groupManager, ConfigurationController configurationController) { return new HeadsUpManagerPhone(context, statusBarStateController, bypassController, groupManager, configurationController); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 3afe7210f073..8f4e738e5a5f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -16,6 +16,7 @@ package com.android.systemui.dagger; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.appops.dagger.AppOpsModule; @@ -42,7 +43,7 @@ import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule; import com.android.systemui.tuner.dagger.TunerModule; -import com.android.systemui.util.concurrency.ConcurrencyModule; +import com.android.systemui.util.concurrency.SysUIConcurrencyModule; import com.android.systemui.util.dagger.UtilModule; import com.android.systemui.util.sensors.SensorModule; import com.android.systemui.util.settings.SettingsUtilModule; @@ -62,7 +63,6 @@ import dagger.Provides; @Module(includes = { AppOpsModule.class, AssistModule.class, - ConcurrencyModule.class, ControlsModule.class, DemoModeModule.class, LogModule.class, @@ -74,6 +74,7 @@ import dagger.Provides; SettingsModule.class, SettingsUtilModule.class, StatusBarPolicyModule.class, + SysUIConcurrencyModule.class, TunerModule.class, UtilModule.class, VolumeModule.class @@ -82,6 +83,7 @@ import dagger.Provides; NotificationRowComponent.class, DozeComponent.class, ExpandableNotificationRowComponent.class, + KeyguardBouncerComponent.class, NotificationShelfComponent.class, FragmentService.FragmentCreator.class}) public abstract class SystemUIModule { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 929b61a3421c..ad90eff3c969 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -16,6 +16,8 @@ package com.android.systemui.dagger; +import javax.inject.Inject; + import dagger.Subcomponent; /** @@ -32,4 +34,19 @@ public interface WMComponent { interface Builder { WMComponent build(); } + + + /** + * Example class used for passing an API to SysUI from WMShell. + * + * TODO: Remove this once real WM classes are ready to go. + **/ + @WMSingleton + class StubAPIClass { + @Inject + StubAPIClass() {} + } + + /** Create a StubAPIClass. */ + StubAPIClass createStubAPIClass(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 342818de3d1e..d9f971731f29 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -30,6 +30,8 @@ import android.os.UserHandle; import android.provider.Settings; import android.view.Display; +import androidx.annotation.Nullable; + import com.android.systemui.doze.dagger.BrightnessSensor; import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.doze.dagger.WrappedService; @@ -75,7 +77,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi @Inject public DozeScreenBrightness(Context context, @WrappedService DozeMachine.Service service, - AsyncSensorManager sensorManager, @BrightnessSensor Sensor lightSensor, + AsyncSensorManager sensorManager, @Nullable @BrightnessSensor Sensor lightSensor, DozeHost host, Handler handler, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy) { mContext = context; mDozeService = service; diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java index 04f7c368fdc4..b89946028aeb 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java @@ -20,6 +20,8 @@ import android.content.Context; import android.hardware.Sensor; import android.os.Handler; +import androidx.annotation.Nullable; + import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeAuthRemover; @@ -91,6 +93,7 @@ public abstract class DozeModule { @Provides @BrightnessSensor + @Nullable static Sensor providesBrightnessSensor(AsyncSensorManager sensorManager, Context context) { return DozeSensors.findSensorWithType(sensorManager, context.getString(R.string.doze_brightness_sensor_type)); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 33407918f938..2705f07069bf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2184,8 +2184,8 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer, View notificationContainer, KeyguardBypassController bypassController) { mKeyguardViewControllerLazy.get().registerStatusBar(statusBar, container, panelView, - biometricUnlockController, mDismissCallbackRegistry, lockIconContainer, - notificationContainer, bypassController, mFalsingManager); + biometricUnlockController, lockIconContainer, + notificationContainer, bypassController); return mKeyguardViewControllerLazy.get(); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index a003d8365810..e5a9ac10389f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -172,7 +172,6 @@ class MediaCarouselController @Inject constructor( // This view is inactive, let's remove this! This happens e.g when dismissing / // timing out a view. We still have the data around because resumption could // be on, but we should save the resources and release this. - oldKey?.let { MediaPlayerData.removeMediaPlayer(it) } onMediaDataRemoved(key) } else { addOrUpdatePlayer(key, oldKey, data) diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index aec3543de4eb..c7e78174f474 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -662,7 +662,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId()); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java index 0354c727c92c..8ef9b092bc00 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java @@ -158,7 +158,9 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Du final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( mDisplaySize.x, mTutorialAreaHeight, 0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); lp.gravity = Gravity.TOP | Gravity.LEFT; lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index e24fbc6cca9d..7dd4edd233bd 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -573,7 +573,12 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, Insets screenInsets, boolean showFlash) { - dismissScreenshot("new screenshot requested", true); + if (mScreenshotLayout.isAttachedToWindow()) { + if (!mDismissAnimation.isRunning()) { // if we didn't already dismiss for another reason + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED); + } + dismissScreenshot("new screenshot requested", true); + } mScreenBitmap = screenshot; diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java index 6b42f2e07bc3..74e0229c4992 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java @@ -59,7 +59,9 @@ public enum ScreenshotEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "screenshot interaction timed out") SCREENSHOT_INTERACTION_TIMEOUT(310), @UiEvent(doc = "screenshot explicitly dismissed") - SCREENSHOT_EXPLICIT_DISMISSAL(311); + SCREENSHOT_EXPLICIT_DISMISSAL(311), + @UiEvent(doc = "screenshot reentered for new screenshot") + SCREENSHOT_REENTERED(640); private final int mId; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 852c05547e32..38c7e5c50f63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -36,12 +36,12 @@ import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.util.Assert; import java.util.ArrayList; @@ -72,7 +72,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle // Dependencies: private final DynamicChildBindController mDynamicChildBindController; protected final NotificationLockscreenUserManager mLockscreenUserManager; - protected final NotificationGroupManager mGroupManager; + protected final NotificationGroupManagerLegacy mGroupManager; protected final VisualStabilityManager mVisualStabilityManager; private final SysuiStatusBarStateController mStatusBarStateController; private final NotificationEntryManager mEntryManager; @@ -107,7 +107,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle Context context, @Main Handler mainHandler, NotificationLockscreenUserManager notificationLockscreenUserManager, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, VisualStabilityManager visualStabilityManager, StatusBarStateController statusBarStateController, NotificationEntryManager notificationEntryManager, @@ -187,13 +187,13 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle ent.setSensitive(sensitive, deviceSensitive); ent.getRow().setNeedsRedaction(needsRedaction); mLowPriorityInflationHelper.recheckLowPriorityViewAndInflate(ent, ent.getRow()); - boolean isChildInGroup = mGroupManager.isChildInGroupWithSummary(ent.getSbn()); + boolean isChildInGroup = mGroupManager.isChildInGroup(ent); boolean groupChangesAllowed = mVisualStabilityManager.areGroupChangesAllowed() // user isn't looking at notifs || !ent.hasFinishedInitialization(); // notif recently added - NotificationEntry parent = mGroupManager.getGroupSummary(ent.getSbn()); + NotificationEntry parent = mGroupManager.getGroupSummary(ent); if (!groupChangesAllowed) { // We don't to change groups while the user is looking at them boolean wasChildInGroup = ent.isChildInGroup(); @@ -431,8 +431,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle while(!stack.isEmpty()) { ExpandableNotificationRow row = stack.pop(); NotificationEntry entry = row.getEntry(); - boolean isChildNotification = - mGroupManager.isChildInGroupWithSummary(entry.getSbn()); + boolean isChildNotification = mGroupManager.isChildInGroup(entry); if (!onKeyguard) { // If mAlwaysExpandNonGroupedNotification is false, then only expand the @@ -448,9 +447,8 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry); if (!showOnKeyguard) { // min priority notifications should show if their summary is showing - if (mGroupManager.isChildInGroupWithSummary(entry.getSbn())) { - NotificationEntry summary = mGroupManager.getLogicalGroupSummary( - entry.getSbn()); + if (mGroupManager.isChildInGroup(entry)) { + NotificationEntry summary = mGroupManager.getLogicalGroupSummary(entry); if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) { showOnKeyguard = true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java index db2875a3d9aa..d15b8476b3c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java @@ -44,12 +44,12 @@ import com.android.systemui.statusbar.notification.DynamicChildBindController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; @@ -157,7 +157,7 @@ public interface StatusBarDependenciesModule { Context context, @Main Handler mainHandler, NotificationLockscreenUserManager notificationLockscreenUserManager, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, VisualStabilityManager visualStabilityManager, StatusBarStateController statusBarStateController, NotificationEntryManager notificationEntryManager, 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 c68625c9d9ee..433c8b0d361d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -30,7 +30,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.stack.StackStateAnimator -import com.android.systemui.statusbar.phone.NotificationGroupManager +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject @@ -64,7 +64,7 @@ class ConversationNotificationProcessor @Inject constructor( @SysUISingleton class ConversationNotificationManager @Inject constructor( private val notificationEntryManager: NotificationEntryManager, - private val notificationGroupManager: NotificationGroupManager, + private val notificationGroupManager: NotificationGroupManagerLegacy, private val context: Context, @Main private val mainHandler: Handler ) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index b5f1c7ff9b62..e1e77b0723a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -48,13 +48,13 @@ import com.android.systemui.statusbar.NotificationUiAdjustment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.notification.logging.NotificationLogger; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.util.Assert; import com.android.systemui.util.leak.LeakDetector; @@ -139,7 +139,7 @@ public class NotificationEntryManager implements private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>(); private final KeyguardEnvironment mKeyguardEnvironment; - private final NotificationGroupManager mGroupManager; + private final NotificationGroupManagerLegacy mGroupManager; private final NotificationRankingManager mRankingManager; private final FeatureFlags mFeatureFlags; private final ForegroundServiceDismissalFeatureController mFgsFeatureController; @@ -199,7 +199,7 @@ public class NotificationEntryManager implements */ public NotificationEntryManager( NotificationEntryManagerLogger logger, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, NotificationRankingManager rankingManager, KeyguardEnvironment keyguardEnvironment, FeatureFlags featureFlags, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index 590ccf830a78..73c7fd1b64a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -34,7 +34,6 @@ import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import javax.inject.Inject; @@ -46,8 +45,6 @@ import javax.inject.Inject; @SysUISingleton public class NotificationFilter { - private final NotificationGroupManager mGroupManager = Dependency.get( - NotificationGroupManager.class); private final StatusBarStateController mStatusBarStateController; private final Boolean mIsMediaFlagEnabled; 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 8ce9d944b865..789e78e33671 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 @@ -60,6 +60,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; @@ -69,7 +70,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.stack.PriorityBucket; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import java.util.ArrayList; import java.util.List; @@ -430,7 +430,7 @@ public final class NotificationEntry extends ListEntry { * Get the children that are actually attached to this notification's row. * * TODO: Seems like most callers here should probably be using - * {@link NotificationGroupManager#getChildren} + * {@link NotificationGroupManagerLegacy#getChildren} */ public @Nullable List<NotificationEntry> getAttachedNotifChildren() { if (row == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index bab2686c5c9c..fb42c424f603 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -33,7 +33,7 @@ import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVI import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.notification.stack.PriorityBucket -import com.android.systemui.statusbar.phone.NotificationGroupManager +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.policy.HeadsUpManager import dagger.Lazy import java.util.Objects @@ -52,7 +52,7 @@ private const val TAG = "NotifRankingManager" */ open class NotificationRankingManager @Inject constructor( private val mediaManagerLazy: Lazy<NotificationMediaManager>, - private val groupManager: NotificationGroupManager, + private val groupManager: NotificationGroupManagerLegacy, private val headsUpManager: HeadsUpManager, private val notifFilter: NotificationFilter, private val logger: NotificationEntryManagerLogger, @@ -191,7 +191,7 @@ open class NotificationRankingManager @Inject constructor( private fun NotificationEntry.isConversation() = getPeopleNotificationType() != TYPE_NON_PERSON private fun NotificationEntry.getPeopleNotificationType() = - peopleNotificationIdentifier.getPeopleNotificationType(sbn, ranking) + peopleNotificationIdentifier.getPeopleNotificationType(this) private fun NotificationEntry.isHighPriority() = highPriorityProvider.isHighPriority(this) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt index dea11626a3f8..3aaa9acdb897 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -53,8 +53,7 @@ class ConversationCoordinator @Inject constructor( } private fun isConversation(entry: NotificationEntry): Boolean = - peopleNotificationIdentifier.getPeopleNotificationType(entry.sbn, entry.ranking) != - TYPE_NON_PERSON + peopleNotificationIdentifier.getPeopleNotificationType(entry) != TYPE_NON_PERSON companion object { private const val TAG = "ConversationCoordinator" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java index 6089aa26fe71..aec26474cf7d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/LowPriorityInflationHelper.java @@ -20,10 +20,10 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.RowContentBindParams; import com.android.systemui.statusbar.notification.row.RowContentBindStage; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import javax.inject.Inject; @@ -34,13 +34,13 @@ import javax.inject.Inject; @SysUISingleton public class LowPriorityInflationHelper { private final FeatureFlags mFeatureFlags; - private final NotificationGroupManager mGroupManager; + private final NotificationGroupManagerLegacy mGroupManager; private final RowContentBindStage mRowContentBindStage; @Inject LowPriorityInflationHelper( FeatureFlags featureFlags, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, RowContentBindStage rowContentBindStage) { mFeatureFlags = featureFlags; mGroupManager = groupManager; @@ -78,7 +78,7 @@ public class LowPriorityInflationHelper { if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { isGroupChild = (entry.getParent() != GroupEntry.ROOT_ENTRY); } else { - isGroupChild = mGroupManager.isChildInGroupWithSummary(entry.getSbn()); + isGroupChild = mGroupManager.isChildInGroup(entry); } return entry.isAmbient() && !isGroupChild; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java index c44c59c02810..21d54c85160b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/NotificationGroupManagerLegacy.java @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.statusbar.notification.collection.legacy; import android.annotation.Nullable; import android.service.notification.StatusBarNotification; @@ -22,14 +22,17 @@ import android.util.ArraySet; import android.util.Log; import com.android.systemui.Dependency; +import com.android.systemui.Dumpable; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -37,6 +40,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -46,13 +50,19 @@ import dagger.Lazy; /** * A class to handle notifications and their corresponding groups. + * This includes: + * 1. Determining whether an entry is a member of a group and whether it is a summary or a child + * 2. Tracking group expansion states */ @SysUISingleton -public class NotificationGroupManager implements OnHeadsUpChangedListener, StateListener { +public class NotificationGroupManagerLegacy implements OnHeadsUpChangedListener, StateListener, + GroupMembershipManager, GroupExpansionManager, Dumpable { private static final String TAG = "NotificationGroupManager"; private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>(); - private final ArraySet<OnGroupChangeListener> mListeners = new ArraySet<>(); + private final ArraySet<OnGroupExpansionChangeListener> mExpansionChangeListeners = + new ArraySet<>(); + private final ArraySet<OnGroupChangeListener> mGroupChangeListeners = new ArraySet<>(); private final Lazy<PeopleNotificationIdentifier> mPeopleNotificationIdentifier; private int mBarState = -1; private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>(); @@ -61,7 +71,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State @Nullable private BubbleController mBubbleController = null; @Inject - public NotificationGroupManager( + public NotificationGroupManagerLegacy( StatusBarStateController statusBarStateController, Lazy<PeopleNotificationIdentifier> peopleNotificationIdentifier) { statusBarStateController.addCallback(this); @@ -77,15 +87,19 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State /** * Add a listener for changes to groups. - * - * @param listener listener to add */ - public void addOnGroupChangeListener(OnGroupChangeListener listener) { - mListeners.add(listener); + public void registerGroupChangeListener(OnGroupChangeListener listener) { + mGroupChangeListeners.add(listener); } - public boolean isGroupExpanded(StatusBarNotification sbn) { - NotificationGroup group = mGroupMap.get(getGroupKey(sbn)); + @Override + public void registerGroupExpansionChangeListener(OnGroupExpansionChangeListener listener) { + mExpansionChangeListeners.add(listener); + } + + @Override + public boolean isGroupExpanded(NotificationEntry entry) { + NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn())); if (group == null) { return false; } @@ -103,8 +117,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return group.expanded; } - public void setGroupExpanded(StatusBarNotification sbn, boolean expanded) { - NotificationGroup group = mGroupMap.get(getGroupKey(sbn)); + @Override + public void setGroupExpanded(NotificationEntry entry, boolean expanded) { + NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn())); if (group == null) { return; } @@ -114,12 +129,15 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State private void setGroupExpanded(NotificationGroup group, boolean expanded) { group.expanded = expanded; if (group.summary != null) { - for (OnGroupChangeListener listener : mListeners) { - listener.onGroupExpansionChanged(group.summary.getRow(), expanded); + for (OnGroupExpansionChangeListener listener : mExpansionChangeListeners) { + listener.onGroupExpansionChange(group.summary.getRow(), expanded); } } } + /** + * When we want to remove an entry from being tracked for grouping + */ public void onEntryRemoved(NotificationEntry removed) { onEntryRemovedInternal(removed, removed.getSbn()); mIsolatedEntries.remove(removed.getKey()); @@ -158,7 +176,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State if (group.children.isEmpty()) { if (group.summary == null) { mGroupMap.remove(groupKey); - for (OnGroupChangeListener listener : mListeners) { + for (OnGroupChangeListener listener : mGroupChangeListeners) { listener.onGroupRemoved(group, groupKey); } } @@ -184,7 +202,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State if (group == null) { group = new NotificationGroup(); mGroupMap.put(groupKey, group); - for (OnGroupChangeListener listener : mListeners) { + + for (OnGroupChangeListener listener : mGroupChangeListeners) { listener.onGroupCreated(group, groupKey); } } @@ -195,9 +214,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State Log.wtf(TAG, "Inconsistent entries found with the same key " + added.getKey() + "existing removed: " + existing.isRowRemoved() + (existingThrowable != null - ? Log.getStackTraceString(existingThrowable) + "\n": "") - + " added removed" + added.isRowRemoved() - , new Throwable()); + ? Log.getStackTraceString(existingThrowable) + "\n" : "") + + " added removed" + added.isRowRemoved(), new Throwable()); } group.children.put(added.getKey(), added); updateSuppression(group); @@ -206,12 +224,12 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State group.expanded = added.areChildrenExpanded(); updateSuppression(group); if (!group.children.isEmpty()) { - ArrayList<NotificationEntry> childrenCopy - = new ArrayList<>(group.children.values()); + ArrayList<NotificationEntry> childrenCopy = + new ArrayList<>(group.children.values()); for (NotificationEntry child : childrenCopy) { onEntryBecomingChild(child); } - for (OnGroupChangeListener listener : mListeners) { + for (OnGroupChangeListener listener : mGroupChangeListeners) { listener.onGroupCreatedFromChildren(group); } } @@ -243,7 +261,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State && group.summary.getSbn().getNotification().isGroupSummary() && (hasIsolatedChildren(group) || hasBubbles))); if (prevSuppressed != group.suppressed) { - for (OnGroupChangeListener listener : mListeners) { + for (OnGroupChangeListener listener : mGroupChangeListeners) { if (!mIsUpdatingUnchangedGroup) { listener.onGroupSuppressionChanged(group, group.suppressed); listener.onGroupsChanged(); @@ -306,6 +324,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } } + /** + * Whether the given notification is the summary of a group that is being suppressed + */ public boolean isSummaryOfSuppressedGroup(StatusBarNotification sbn) { return isGroupSuppressed(getGroupKey(sbn)) && sbn.getNotification().isGroupSummary(); } @@ -315,13 +336,14 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State && getTotalNumberOfChildren(sbn) == 1; } - public boolean isOnlyChildInGroup(StatusBarNotification sbn) { + @Override + public boolean isOnlyChildInGroup(NotificationEntry entry) { + final StatusBarNotification sbn = entry.getSbn(); if (!isOnlyChild(sbn)) { return false; } - NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn); - return logicalGroupSummary != null - && !logicalGroupSummary.getSbn().equals(sbn); + NotificationEntry logicalGroupSummary = getLogicalGroupSummary(entry); + return logicalGroupSummary != null && !logicalGroupSummary.getSbn().equals(sbn); } private int getTotalNumberOfChildren(StatusBarNotification sbn) { @@ -339,11 +361,12 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State private void setStatusBarState(int newState) { mBarState = newState; if (mBarState == StatusBarState.KEYGUARD) { - collapseAllGroups(); + collapseGroups(); } } - public void collapseAllGroups() { + @Override + public void collapseGroups() { // Because notifications can become isolated when the group becomes suppressed it can // lead to concurrent modifications while looping. We need to make a copy. ArrayList<NotificationGroup> groupCopy = new ArrayList<>(mGroupMap.values()); @@ -357,10 +380,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } } - /** - * @return whether a given notification is a child in a group which has a summary - */ - public boolean isChildInGroupWithSummary(StatusBarNotification sbn) { + @Override + public boolean isChildInGroup(NotificationEntry entry) { + final StatusBarNotification sbn = entry.getSbn(); if (!isGroupChild(sbn)) { return false; } @@ -377,10 +399,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return true; } - /** - * @return whether a given notification is a summary in a group which has children - */ - public boolean isSummaryOfGroup(StatusBarNotification sbn) { + @Override + public boolean isGroupSummary(NotificationEntry entry) { + final StatusBarNotification sbn = entry.getSbn(); if (!isGroupSummary(sbn)) { return false; } @@ -391,21 +412,14 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return !group.children.isEmpty() && Objects.equals(group.summary.getSbn(), sbn); } - /** - * Get the summary of a specified status bar notification. For isolated notification this return - * itself. - */ - public NotificationEntry getGroupSummary(StatusBarNotification sbn) { - return getGroupSummary(getGroupKey(sbn)); + @Override + public NotificationEntry getGroupSummary(NotificationEntry entry) { + return getGroupSummary(getGroupKey(entry.getSbn())); } - /** - * Similar to {@link #getGroupSummary(StatusBarNotification)} but doesn't get the visual summary - * but the logical summary, i.e when a child is isolated, it still returns the summary as if - * it wasn't isolated. - */ - public NotificationEntry getLogicalGroupSummary(StatusBarNotification sbn) { - return getGroupSummary(sbn.getGroupKey()); + @Override + public NotificationEntry getLogicalGroupSummary(NotificationEntry entry) { + return getGroupSummary(entry.getSbn().getGroupKey()); } @Nullable @@ -436,14 +450,10 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return children; } - /** - * Get the children that are in the summary's group, not including those isolated. - * - * @param summary summary of a group - * @return list of the children - */ - public @Nullable ArrayList<NotificationEntry> getChildren(StatusBarNotification summary) { - NotificationGroup group = mGroupMap.get(summary.getGroupKey()); + @Override + public @Nullable List<NotificationEntry> getChildren(ListEntry listEntrySummary) { + NotificationEntry summary = listEntrySummary.getRepresentativeEntry(); + NotificationGroup group = mGroupMap.get(summary.getSbn().getGroupKey()); if (group == null) { return null; } @@ -479,9 +489,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State return groupKey; } - /** @return group expansion state after toggling. */ - public boolean toggleGroupExpansion(StatusBarNotification sbn) { - NotificationGroup group = mGroupMap.get(getGroupKey(sbn)); + @Override + public boolean toggleGroupExpansion(NotificationEntry entry) { + NotificationGroup group = mGroupMap.get(getGroupKey(entry.getSbn())); if (group == null) { return false; } @@ -494,10 +504,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State } /** - * Whether a notification is visually a group summary. - * - * @param sbn notification to check - * @return true if it is visually a group summary + * Is this notification the summary of a group? */ public boolean isGroupSummary(StatusBarNotification sbn) { if (isIsolated(sbn.getKey())) { @@ -536,14 +543,13 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * @param entry the notification to check * @return true if the entry should be isolated */ - private boolean shouldIsolate(NotificationEntry entry) { StatusBarNotification sbn = entry.getSbn(); if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) { return false; } - int peopleNotificationType = mPeopleNotificationIdentifier.get().getPeopleNotificationType( - entry.getSbn(), entry.getRanking()); + int peopleNotificationType = + mPeopleNotificationIdentifier.get().getPeopleNotificationType(entry); if (peopleNotificationType == PeopleNotificationIdentifier.TYPE_IMPORTANT_PERSON) { return true; } @@ -576,7 +582,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State // When the notification gets added afterwards it is already isolated and therefore // it doesn't lead to an update. updateSuppression(mGroupMap.get(entry.getSbn().getGroupKey())); - for (OnGroupChangeListener listener : mListeners) { + for (OnGroupChangeListener listener : mGroupChangeListeners) { listener.onGroupsChanged(); } } @@ -607,7 +613,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State onEntryRemovedInternal(entry, entry.getSbn()); mIsolatedEntries.remove(sbn.getKey()); onEntryAddedInternal(entry); - for (OnGroupChangeListener listener : mListeners) { + for (OnGroupChangeListener listener : mGroupChangeListeners) { listener.onGroupsChanged(); } } @@ -618,12 +624,16 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State || notificationGroup.summary.isGroupNotFullyVisible(); } + /** + * Directly set the heads up manager to avoid circular dependencies + */ public void setHeadsUpManager(HeadsUpManager headsUpManager) { mHeadsUpManager = headsUpManager; } + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println("GroupManager state:"); + pw.println("GroupManagerLegacy state:"); pw.println(" number of groups: " + mGroupMap.size()); for (Map.Entry<String, NotificationGroup> entry : mGroupMap.entrySet()) { pw.println("\n key: " + entry.getKey()); pw.println(entry.getValue()); @@ -640,6 +650,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State setStatusBarState(newState); } + /** + * Represents a notification group in the notification shade. + */ public static class NotificationGroup { public final HashMap<String, NotificationEntry> children = new HashMap<>(); public NotificationEntry summary; @@ -659,24 +672,29 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State result += "\n children size: " + children.size(); for (NotificationEntry child : children.values()) { result += "\n " + child.getSbn() - + (child.getDebugThrowable() != null - ? Log.getStackTraceString(child.getDebugThrowable()) - : ""); + + (child.getDebugThrowable() != null + ? Log.getStackTraceString(child.getDebugThrowable()) + : ""); } result += "\n summary suppressed: " + suppressed; return result; } } + /** + * Listener for group changes not including group expansion changes which are handled by + * {@link OnGroupExpansionChangeListener}. + */ public interface OnGroupChangeListener { - /** * A new group has been created. * * @param group the group that was created * @param groupKey the group's key */ - default void onGroupCreated(NotificationGroup group, String groupKey) {} + default void onGroupCreated( + NotificationGroup group, + String groupKey) {} /** * A group has been removed. @@ -684,7 +702,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * @param group the group that was removed * @param groupKey the group's key */ - default void onGroupRemoved(NotificationGroup group, String groupKey) {} + default void onGroupRemoved( + NotificationGroup group, + String groupKey) {} /** * The suppression of a group has changed. @@ -692,16 +712,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State * @param group the group that has changed * @param suppressed true if the group is now suppressed, false o/w */ - default void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {} - - /** - * The expansion of a group has changed. - * - * @param changedRow the row for which the expansion has changed, which is also the summary - * @param expanded a boolean indicating the new expanded state - */ - default void onGroupExpansionChanged(ExpandableNotificationRow changedRow, - boolean expanded) {} + default void onGroupSuppressionChanged( + NotificationGroup group, + boolean suppressed) {} /** * A group of children just received a summary notification and should therefore become diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java index 8b803b517f14..18806effc545 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProvider.java @@ -20,11 +20,10 @@ import android.app.Notification; import android.app.NotificationManager; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import java.util.List; @@ -39,14 +38,14 @@ import javax.inject.Inject; @SysUISingleton public class HighPriorityProvider { private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; - private final NotificationGroupManager mGroupManager; + private final GroupMembershipManager mGroupMembershipManager; @Inject public HighPriorityProvider( PeopleNotificationIdentifier peopleNotificationIdentifier, - NotificationGroupManager groupManager) { + GroupMembershipManager groupManager) { mPeopleNotificationIdentifier = peopleNotificationIdentifier; - mGroupManager = groupManager; + mGroupMembershipManager = groupManager; } /** @@ -81,20 +80,15 @@ public class HighPriorityProvider { private boolean hasHighPriorityChild(ListEntry entry) { - List<NotificationEntry> children = null; - - if (entry instanceof GroupEntry) { - // New notification pipeline - children = ((GroupEntry) entry).getChildren(); - } else if (entry.getRepresentativeEntry() != null - && mGroupManager.isGroupSummary(entry.getRepresentativeEntry().getSbn())) { - // Old notification pipeline - children = mGroupManager.getChildren(entry.getRepresentativeEntry().getSbn()); + if (entry instanceof NotificationEntry + && !mGroupMembershipManager.isGroupSummary((NotificationEntry) entry)) { + return false; } + List<NotificationEntry> children = mGroupMembershipManager.getChildren(entry); if (children != null) { for (NotificationEntry child : children) { - if (isHighPriority(child)) { + if (child != entry && isHighPriority(child)) { return true; } } @@ -122,8 +116,8 @@ public class HighPriorityProvider { } private boolean isPeopleNotification(NotificationEntry entry) { - return mPeopleNotificationIdentifier.getPeopleNotificationType( - entry.getSbn(), entry.getRanking()) != PeopleNotificationIdentifier.TYPE_NON_PERSON; + return mPeopleNotificationIdentifier.getPeopleNotificationType(entry) + != PeopleNotificationIdentifier.TYPE_NON_PERSON; } private boolean hasUserSetImportance(NotificationEntry entry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManager.java new file mode 100644 index 000000000000..d2df07ed7864 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManager.java @@ -0,0 +1,66 @@ +/* + * 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.systemui.statusbar.notification.collection.render; + +import com.android.systemui.Dumpable; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; + +/** + * Tracks expanded notification states for groups. This expanded state should not be confused by the + * expanded/collapsed state of a single notification which is tracked within each + * ExpandableNotificationRow. + */ +public interface GroupExpansionManager extends Dumpable { + + /** + * Register a listener for group expansion changes + */ + void registerGroupExpansionChangeListener(OnGroupExpansionChangeListener listener); + + /** + * Whether the group associated with this notification is expanded. + * If this notification is not part of a group, it will always return false. + */ + boolean isGroupExpanded(NotificationEntry entry); + + /** + * Set whether the group associated with this notification is expanded or not. + */ + void setGroupExpanded(NotificationEntry entry, boolean expanded); + + /** @return group expansion state after toggling. */ + boolean toggleGroupExpansion(NotificationEntry entry); + + /** + * Set expanded=false for all groups + */ + void collapseGroups(); + + /** + * Listener for group expansion changes. + */ + interface OnGroupExpansionChangeListener { + /** + * The expansion of a group has changed. + * + * @param changedRow the row for which the expansion has changed, which is also the summary + * @param expanded a boolean indicating the new expanded state + */ + void onGroupExpansionChange(ExpandableNotificationRow changedRow, boolean expanded); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java new file mode 100644 index 000000000000..b9aa26f75c9b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java @@ -0,0 +1,113 @@ +/* + * 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.systemui.statusbar.notification.collection.render; + +import com.android.systemui.statusbar.notification.collection.GroupEntry; +import com.android.systemui.statusbar.notification.collection.ListEntry; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.coordinator.Coordinator; +import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.HashSet; +import java.util.Set; + +/** + * Provides grouping information for notification entries including information about a group's + * expanded state. + */ +public class GroupExpansionManagerImpl implements GroupExpansionManager, Coordinator { + private final GroupMembershipManager mGroupMembershipManager; + private final Set<OnGroupExpansionChangeListener> mOnGroupChangeListeners = new HashSet<>(); + + // Set of summary keys whose groups are expanded + private final Set<NotificationEntry> mExpandedGroups = new HashSet<>(); + + public GroupExpansionManagerImpl(GroupMembershipManager groupMembershipManager) { + mGroupMembershipManager = groupMembershipManager; + } + + /** + * Cleanup entries from mExpandedGroups that no longer exist in the pipeline. + */ + private final OnBeforeRenderListListener mNotifTracker = (entries) -> { + final Set<NotificationEntry> renderingSummaries = new HashSet<>(); + for (ListEntry entry : entries) { + if (entry instanceof GroupEntry) { + renderingSummaries.add(entry.getRepresentativeEntry()); + } + } + mExpandedGroups.removeIf(expandedGroup -> !renderingSummaries.contains(expandedGroup)); + }; + + @Override + public void attach(NotifPipeline pipeline) { + pipeline.addOnBeforeRenderListListener(mNotifTracker); + } + + @Override + public void registerGroupExpansionChangeListener(OnGroupExpansionChangeListener listener) { + mOnGroupChangeListeners.add(listener); + } + + @Override + public boolean isGroupExpanded(NotificationEntry entry) { + return mExpandedGroups.contains(mGroupMembershipManager.getGroupSummary(entry)); + } + + @Override + public void setGroupExpanded(NotificationEntry entry, boolean expanded) { + final NotificationEntry groupSummary = mGroupMembershipManager.getGroupSummary(entry); + if (expanded) { + mExpandedGroups.add(groupSummary); + } else { + mExpandedGroups.remove(groupSummary); + } + + sendOnGroupExpandedChange(entry, expanded); + } + + @Override + public boolean toggleGroupExpansion(NotificationEntry entry) { + setGroupExpanded(entry, !isGroupExpanded(entry)); + return isGroupExpanded(entry); + } + + @Override + public void collapseGroups() { + for (NotificationEntry entry : mExpandedGroups) { + setGroupExpanded(entry, false); + } + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("NotificationEntryExpansion state:"); + pw.println(" # expanded groups: " + mExpandedGroups.size()); + for (NotificationEntry entry : mExpandedGroups) { + pw.println(" summary key of expanded group: " + entry.getKey()); + } + } + + private void sendOnGroupExpandedChange(NotificationEntry entry, boolean expanded) { + for (OnGroupExpansionChangeListener listener : mOnGroupChangeListeners) { + listener.onGroupExpansionChange(entry.getRow(), expanded); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java new file mode 100644 index 000000000000..196cb8f26b22 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManager.java @@ -0,0 +1,73 @@ +/* + * 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.systemui.statusbar.notification.collection.render; + +import android.annotation.Nullable; + +import com.android.systemui.statusbar.notification.collection.ListEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import java.util.List; + +/** + * Helper that determines the group states (parent, summary, children) of a notification. + */ +public interface GroupMembershipManager { + /** + * @return whether a given notification is a top level entry or is the summary in a group which + * has children + */ + boolean isGroupSummary(NotificationEntry entry); + + /** + * Get the summary of a specified status bar notification. For an isolated notification this + * returns itself. + */ + NotificationEntry getGroupSummary(NotificationEntry entry); + + /** + * Similar to {@link #getGroupSummary(NotificationEntry)} but doesn't get the visual summary + * but the logical summary, i.e when a child is isolated, it still returns the summary as if + * it wasn't isolated. + * TODO: remove this when migrating to the new pipeline, this is taken care of in the + * dismissal logic built into NotifCollection + */ + default NotificationEntry getLogicalGroupSummary(NotificationEntry entry) { + return getGroupSummary(entry); + } + + /** + * @return whether a given notification is a child in a group + */ + boolean isChildInGroup(NotificationEntry entry); + + /** + * Whether this is the only child in a group + * TODO: remove this when migrating to the new pipeline, this is taken care of in the + * dismissal logic built into NotifCollection + */ + boolean isOnlyChildInGroup(NotificationEntry entry); + + /** + * Get the children that are in the summary's group, not including those isolated. + * + * @param summary summary of a group + * @return list of the children + */ + @Nullable + List<NotificationEntry> getChildren(ListEntry summary); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java new file mode 100644 index 000000000000..c1f468a3072f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupMembershipManagerImpl.java @@ -0,0 +1,80 @@ +/* + * 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.systemui.statusbar.notification.collection.render; + +import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY; + +import androidx.annotation.Nullable; + +import com.android.systemui.statusbar.notification.collection.GroupEntry; +import com.android.systemui.statusbar.notification.collection.ListEntry; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import java.util.List; + +/** + * ShadeListBuilder groups notifications from system server. This manager translates + * ShadeListBuilder's method of grouping to be used within SystemUI. + */ +public class GroupMembershipManagerImpl implements GroupMembershipManager { + @Override + public boolean isGroupSummary(NotificationEntry entry) { + return getGroupSummary(entry) == entry; + } + + @Override + public NotificationEntry getGroupSummary(NotificationEntry entry) { + if (isEntryTopLevel(entry) || entry.getParent() == null) { + return null; + } + + return entry.getParent().getRepresentativeEntry(); + } + + @Override + public boolean isChildInGroup(NotificationEntry entry) { + return !isEntryTopLevel(entry); + } + + @Override + public boolean isOnlyChildInGroup(NotificationEntry entry) { + if (entry.getParent() == null) { + return false; + } + + return entry.getParent().getChildren().size() == 1; + } + + @Nullable + @Override + public List<NotificationEntry> getChildren(ListEntry entry) { + if (entry instanceof GroupEntry) { + return ((GroupEntry) entry).getChildren(); + } + + if (isGroupSummary(entry.getRepresentativeEntry())) { + // maybe we were actually passed the summary + return entry.getRepresentativeEntry().getParent().getChildren(); + } + + return null; + } + + private boolean isEntryTopLevel(NotificationEntry entry) { + return entry.getParent() == ROOT_ENTRY; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt index 3c35b7bd8472..5243854ea412 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt @@ -24,7 +24,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.ShadeListBuilder import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.NotificationIconAreaController -import java.lang.RuntimeException import javax.inject.Inject /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index f3ed95bd2d76..e2aae64ce220 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -50,10 +50,15 @@ import com.android.systemui.statusbar.notification.collection.coordinator.Visual import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManagerImpl; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl; import com.android.systemui.statusbar.notification.init.NotificationsControllerStub; @@ -67,7 +72,6 @@ import com.android.systemui.statusbar.notification.row.NotificationBlockingHelpe import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; @@ -91,7 +95,7 @@ public interface NotificationsModule { @Provides static NotificationEntryManager provideNotificationEntryManager( NotificationEntryManagerLogger logger, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, NotificationRankingManager rankingManager, NotificationEntryManager.KeyguardEnvironment keyguardEnvironment, FeatureFlags featureFlags, @@ -206,6 +210,29 @@ public interface NotificationsModule { context, notificationGutsManager, notificationEntryManager, metricsLogger); } + /** Provides an instance of {@link GroupMembershipManager} */ + @SysUISingleton + @Provides + static GroupMembershipManager provideGroupMembershipManager( + FeatureFlags featureFlags, + Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) { + return featureFlags.isNewNotifPipelineRenderingEnabled() + ? new GroupMembershipManagerImpl() + : groupManagerLegacy.get(); + } + + /** Provides an instance of {@link GroupExpansionManager} */ + @SysUISingleton + @Provides + static GroupExpansionManager provideGroupExpansionManager( + FeatureFlags featureFlags, + Lazy<GroupMembershipManager> groupMembershipManager, + Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) { + return featureFlags.isNewNotifPipelineRenderingEnabled() + ? new GroupExpansionManagerImpl(groupMembershipManager.get()) + : groupManagerLegacy.get(); + } + /** Initializes the notification data pipeline (can be disabled via config). */ @SysUISingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 6460892952e7..9fb292878553 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -30,12 +30,12 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.TargetSdkResolver import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.interruption.HeadsUpController import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper -import com.android.systemui.statusbar.phone.NotificationGroupManager import com.android.systemui.statusbar.phone.StatusBar import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.HeadsUpManager @@ -65,7 +65,7 @@ class NotificationsControllerImpl @Inject constructor( private val deviceProvisionedController: DeviceProvisionedController, private val notificationRowBinder: NotificationRowBinderImpl, private val remoteInputUriController: RemoteInputUriController, - private val groupManager: NotificationGroupManager, + private val groupManagerLegacy: Lazy<NotificationGroupManagerLegacy>, private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper, private val headsUpManager: HeadsUpManager, private val headsUpController: HeadsUpController, @@ -111,11 +111,11 @@ class NotificationsControllerImpl @Inject constructor( } else { targetSdkResolver.initialize(entryManager) remoteInputUriController.attach(entryManager) - groupAlertTransferHelper.bind(entryManager, groupManager) - headsUpManager.addListener(groupManager) + groupAlertTransferHelper.bind(entryManager, groupManagerLegacy.get()) + headsUpManager.addListener(groupManagerLegacy.get()) headsUpManager.addListener(groupAlertTransferHelper) headsUpController.attach(entryManager, headsUpManager) - groupManager.setHeadsUpManager(headsUpManager) + groupManagerLegacy.get().setHeadsUpManager(headsUpManager) groupAlertTransferHelper.setHeadsUpManager(headsUpManager) entryManager.attach(notificationListener) @@ -131,7 +131,6 @@ class NotificationsControllerImpl @Inject constructor( if (dumpTruck) { entryManager.dump(pw, " ") } - groupManager.dump(fd, pw, args) } // TODO: Convert all functions below this line into listeners instead of public methods diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt index 743bf332fc9d..99b2fcc9d610 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt @@ -214,7 +214,7 @@ class PeopleHubDataSourceImpl @Inject constructor( } private fun NotificationEntry.extractPerson(): PersonModel? { - val type = peopleNotificationIdentifier.getPeopleNotificationType(sbn, ranking) + val type = peopleNotificationIdentifier.getPeopleNotificationType(this) if (type == TYPE_NON_PERSON) { return null } @@ -249,7 +249,7 @@ class PeopleHubDataSourceImpl @Inject constructor( private fun NotificationEntry.extractPersonKey(): PersonKey? { // TODO migrate to shortcut id when snoozing is conversation wide - val type = peopleNotificationIdentifier.getPeopleNotificationType(sbn, ranking) + val type = peopleNotificationIdentifier.getPeopleNotificationType(this) return if (type != TYPE_NON_PERSON) key else null } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt index 1ac2cb5a36d5..0d92616767f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt @@ -20,12 +20,13 @@ import android.annotation.IntDef import android.service.notification.NotificationListenerService.Ranking import android.service.notification.StatusBarNotification import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON -import com.android.systemui.statusbar.phone.NotificationGroupManager import javax.inject.Inject import kotlin.math.max @@ -40,10 +41,12 @@ interface PeopleNotificationIdentifier { * that users shortcuts. */ @PeopleNotificationType - fun getPeopleNotificationType(sbn: StatusBarNotification, ranking: Ranking): Int + fun getPeopleNotificationType(entry: NotificationEntry): Int - fun compareTo(@PeopleNotificationType a: Int, - @PeopleNotificationType b: Int): Int + fun compareTo( + @PeopleNotificationType a: Int, + @PeopleNotificationType b: Int + ): Int companion object { @@ -62,24 +65,27 @@ interface PeopleNotificationIdentifier { @SysUISingleton class PeopleNotificationIdentifierImpl @Inject constructor( private val personExtractor: NotificationPersonExtractor, - private val groupManager: NotificationGroupManager + private val groupManager: GroupMembershipManager ) : PeopleNotificationIdentifier { @PeopleNotificationType - override fun getPeopleNotificationType(sbn: StatusBarNotification, ranking: Ranking): Int = - when (val type = ranking.personTypeInfo) { + override fun getPeopleNotificationType(entry: NotificationEntry): Int = + when (val type = entry.ranking.personTypeInfo) { TYPE_IMPORTANT_PERSON -> TYPE_IMPORTANT_PERSON else -> { - when (val type = upperBound(type, extractPersonTypeInfo(sbn))) { + when (val type = upperBound(type, extractPersonTypeInfo(entry.sbn))) { TYPE_IMPORTANT_PERSON -> TYPE_IMPORTANT_PERSON - else -> upperBound(type, getPeopleTypeOfSummary(sbn)) + else -> upperBound(type, getPeopleTypeOfSummary(entry)) } } } - override fun compareTo(@PeopleNotificationType a: Int, - @PeopleNotificationType b: Int): Int { - return b.compareTo(a); + override fun compareTo( + @PeopleNotificationType a: Int, + @PeopleNotificationType b: Int + ): Int + { + return b.compareTo(a) } /** @@ -105,14 +111,14 @@ class PeopleNotificationIdentifierImpl @Inject constructor( private fun extractPersonTypeInfo(sbn: StatusBarNotification) = if (personExtractor.isPersonNotification(sbn)) TYPE_PERSON else TYPE_NON_PERSON - private fun getPeopleTypeOfSummary(statusBarNotification: StatusBarNotification): Int { - if (!groupManager.isSummaryOfGroup(statusBarNotification)) { + private fun getPeopleTypeOfSummary(entry: NotificationEntry): Int { + if (!groupManager.isGroupSummary(entry)) { return TYPE_NON_PERSON } - val childTypes = groupManager.getChildren(statusBarNotification) + val childTypes = groupManager.getChildren(entry) ?.asSequence() - ?.map { getPeopleNotificationType(it.sbn, it.ranking) } + ?.map { getPeopleNotificationType(it) } ?: return TYPE_NON_PERSON var groupType = TYPE_NON_PERSON 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 46b497339d94..89f720535402 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 @@ -86,6 +86,8 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.logging.NotificationCounters; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -97,7 +99,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.SwipeableView; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; @@ -220,7 +221,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mNeedsRedaction; private boolean mLastChronometerRunning = true; private ViewStub mChildrenContainerStub; - private NotificationGroupManager mGroupManager; + private GroupMembershipManager mGroupMembershipManager; + private GroupExpansionManager mGroupExpansionManager; private boolean mChildrenExpanded; private boolean mIsSummaryWithChildren; private NotificationChildrenContainer mChildrenContainer; @@ -269,10 +271,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public void onClick(View v) { if (!shouldShowPublic() && (!mIsLowPriority || isExpanded()) - && mGroupManager.isSummaryOfGroup(mEntry.getSbn())) { + && mGroupMembershipManager.isGroupSummary(mEntry)) { mGroupExpansionChanging = true; - final boolean wasExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn()); - boolean nowExpanded = mGroupManager.toggleGroupExpansion(mEntry.getSbn()); + final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry); + boolean nowExpanded = mGroupExpansionManager.toggleGroupExpansion(mEntry); mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded); MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTIFICATION_GROUP_EXPANDER, nowExpanded); @@ -527,8 +529,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } private boolean isConversation() { - return mPeopleNotificationIdentifier - .getPeopleNotificationType(mEntry.getSbn(), mEntry.getRanking()) + return mPeopleNotificationIdentifier.getPeopleNotificationType(mEntry) != PeopleNotificationIdentifier.TYPE_NON_PERSON; } @@ -820,7 +821,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * @return whether this notification is the only child in the group summary */ public boolean isOnlyChildInGroup() { - return mGroupManager.isOnlyChildInGroup(mEntry.getSbn()); + return mGroupMembershipManager.isOnlyChildInGroup(mEntry); } public ExpandableNotificationRow getNotificationParent() { @@ -1425,8 +1426,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public void performDismiss(boolean fromAccessibility) { if (isOnlyChildInGroup()) { - NotificationEntry groupSummary = - mGroupManager.getLogicalGroupSummary(mEntry.getSbn()); + NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(mEntry); if (groupSummary.isClearable()) { // If this is the only child in the group, dismiss the group, but don't try to show // the blocking helper affordance! @@ -1579,7 +1579,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView String notificationKey, ExpansionLogger logger, KeyguardBypassController bypassController, - NotificationGroupManager groupManager, + GroupMembershipManager groupMembershipManager, + GroupExpansionManager groupExpansionManager, HeadsUpManager headsUpManager, RowContentBindStage rowContentBindStage, OnExpandClickListener onExpandClickListener, @@ -1600,8 +1601,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mLogger = logger; mLoggingKey = notificationKey; mBypassController = bypassController; - mGroupManager = groupManager; - mPrivateLayout.setGroupManager(groupManager); + mGroupMembershipManager = groupMembershipManager; + mGroupExpansionManager = groupExpansionManager; + mPrivateLayout.setGroupMembershipManager(groupMembershipManager); mHeadsUpManager = headsUpManager; mRowContentBindStage = rowContentBindStage; mOnExpandClickListener = onExpandClickListener; @@ -2184,8 +2186,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mFalsingManager.setNotificationExpanded(); if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion && !mChildrenContainer.showingAsLowPriority()) { - final boolean wasExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn()); - mGroupManager.setGroupExpanded(mEntry.getSbn(), userExpanded); + final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry); + mGroupExpansionManager.setGroupExpanded(mEntry, userExpanded); onExpansionChanged(true /* userAction */, wasExpanded); return; } @@ -2328,7 +2330,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public boolean isGroupExpanded() { - return mGroupManager.isGroupExpanded(mEntry.getSbn()); + return mGroupExpansionManager.isGroupExpanded(mEntry); } private void onAttachedChildrenCountChanged() { @@ -2574,7 +2576,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public void makeActionsVisibile() { setUserExpanded(true, true); if (isChildInGroup()) { - mGroupManager.setGroupExpanded(mEntry.getSbn(), true); + mGroupExpansionManager.setGroupExpanded(mEntry, true); } notifyHeightChanged(false /* needsAnimation */); } @@ -2867,7 +2869,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public void onExpandedByGesture(boolean userExpanded) { int event = MetricsEvent.ACTION_NOTIFICATION_GESTURE_EXPANDER; - if (mGroupManager.isSummaryOfGroup(mEntry.getSbn())) { + if (mGroupMembershipManager.isGroupSummary(mEntry)) { event = MetricsEvent.ACTION_NOTIFICATION_GROUP_GESTURE_EXPANDER; } MetricsLogger.action(mContext, event, userExpanded); @@ -2912,7 +2914,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private void onExpansionChanged(boolean userAction, boolean wasExpanded) { boolean nowExpanded = isExpanded(); if (mIsSummaryWithChildren && (!mIsLowPriority || wasExpanded)) { - nowExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn()); + nowExpanded = mGroupExpansionManager.isGroupExpanded(mEntry); } if (nowExpanded != wasExpanded) { updateShelfIconColor(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index ce760cb525f9..05d9fe757dfd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -31,6 +31,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.collection.render.NodeController; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; @@ -39,7 +41,6 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationKey; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.time.SystemClock; @@ -62,7 +63,8 @@ public class ExpandableNotificationRowController implements NodeController { private final String mAppName; private final String mNotificationKey; private final KeyguardBypassController mKeyguardBypassController; - private final NotificationGroupManager mNotificationGroupManager; + private final GroupMembershipManager mGroupMembershipManager; + private final GroupExpansionManager mGroupExpansionManager; private final RowContentBindStage mRowContentBindStage; private final NotificationLogger mNotificationLogger; private final HeadsUpManager mHeadsUpManager; @@ -85,7 +87,8 @@ public class ExpandableNotificationRowController implements NodeController { NotificationMediaManager mediaManager, PluginManager pluginManager, SystemClock clock, @AppName String appName, @NotificationKey String notificationKey, KeyguardBypassController keyguardBypassController, - NotificationGroupManager notificationGroupManager, + GroupMembershipManager groupMembershipManager, + GroupExpansionManager groupExpansionManager, RowContentBindStage rowContentBindStage, NotificationLogger notificationLogger, HeadsUpManager headsUpManager, ExpandableNotificationRow.OnExpandClickListener onExpandClickListener, @@ -103,7 +106,8 @@ public class ExpandableNotificationRowController implements NodeController { mAppName = appName; mNotificationKey = notificationKey; mKeyguardBypassController = keyguardBypassController; - mNotificationGroupManager = notificationGroupManager; + mGroupMembershipManager = groupMembershipManager; + mGroupExpansionManager = groupExpansionManager; mRowContentBindStage = rowContentBindStage; mNotificationLogger = notificationLogger; mHeadsUpManager = headsUpManager; @@ -128,7 +132,8 @@ public class ExpandableNotificationRowController implements NodeController { mNotificationKey, mExpansionLogger, mKeyguardBypassController, - mNotificationGroupManager, + mGroupMembershipManager, + mGroupExpansionManager, mHeadsUpManager, mRowContentBindStage, mOnExpandClickListener, 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 c7e44c5e8e0a..1de9308a40b1 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 @@ -28,9 +28,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.provider.Settings; -import android.service.notification.StatusBarNotification; import android.util.ArrayMap; -import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; @@ -52,11 +50,10 @@ import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper; -import com.android.systemui.statusbar.notification.row.wrapper.NotificationTemplateViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.InflatedSmartReplies; import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; import com.android.systemui.statusbar.policy.RemoteInputView; @@ -122,8 +119,8 @@ public class NotificationContentView extends FrameLayout { private int mSmallHeight; private int mHeadsUpHeight; private int mNotificationMaxHeight; - private StatusBarNotification mStatusBarNotification; - private NotificationGroupManager mGroupManager; + private NotificationEntry mNotificationEntry; + private GroupMembershipManager mGroupMembershipManager; private RemoteInputController mRemoteInputController; private Runnable mExpandedVisibleListener; private PeopleNotificationIdentifier mPeopleIdentifier; @@ -778,7 +775,7 @@ public class NotificationContentView extends FrameLayout { } private boolean isGroupExpanded() { - return mGroupManager.isGroupExpanded(mStatusBarNotification); + return mContainingNotification.isGroupExpanded(); } public void setClipTopAmount(int clipTopAmount) { @@ -908,10 +905,10 @@ public class NotificationContentView extends FrameLayout { public int getBackgroundColorForExpansionState() { // When expanding or user locked we want the new type, when collapsing we want // the original type - final int visibleType = (mContainingNotification.isGroupExpanded() - || mContainingNotification.isUserLocked()) - ? calculateVisibleType() - : getVisibleType(); + final int visibleType = ( + isGroupExpanded() || mContainingNotification.isUserLocked()) + ? calculateVisibleType() + : getVisibleType(); return getBackgroundColor(visibleType); } @@ -1145,7 +1142,7 @@ public class NotificationContentView extends FrameLayout { } public void onNotificationUpdated(NotificationEntry entry) { - mStatusBarNotification = entry.getSbn(); + mNotificationEntry = entry; mBeforeN = entry.targetSdk < Build.VERSION_CODES.N; updateAllSingleLineViews(); ExpandableNotificationRow row = entry.getRow(); @@ -1176,7 +1173,7 @@ public class NotificationContentView extends FrameLayout { if (mIsChildInGroup) { boolean isNewView = mSingleLineView == null; mSingleLineView = mHybridGroupManager.bindFromNotification( - mSingleLineView, mContractedChild, mStatusBarNotification, this); + mSingleLineView, mContractedChild, mNotificationEntry.getSbn(), this); if (isNewView) { updateViewVisibility(mVisibleType, VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView); @@ -1363,7 +1360,7 @@ public class NotificationContentView extends FrameLayout { return; } boolean isPersonWithShortcut = - mPeopleIdentifier.getPeopleNotificationType(entry.getSbn(), entry.getRanking()) + mPeopleIdentifier.getPeopleNotificationType(entry) >= PeopleNotificationIdentifier.TYPE_FULL_PERSON; boolean showButton = isBubblesEnabled() && isPersonWithShortcut @@ -1516,8 +1513,8 @@ public class NotificationContentView extends FrameLayout { } } - public void setGroupManager(NotificationGroupManager groupManager) { - mGroupManager = groupManager; + public void setGroupMembershipManager(GroupMembershipManager groupMembershipManager) { + mGroupMembershipManager = groupMembershipManager; } public void setRemoteInputController(RemoteInputController r) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index 205cecc92e55..65a72cc0b76b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -24,7 +24,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.Nullable; -import android.app.Notification; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; @@ -260,8 +259,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } mFeedbackItem = createFeedbackItem(mContext); NotificationEntry entry = mParent.getEntry(); - int personNotifType = mPeopleNotificationIdentifier - .getPeopleNotificationType(entry.getSbn(), entry.getRanking()); + int personNotifType = mPeopleNotificationIdentifier.getPeopleNotificationType(entry); if (personNotifType == PeopleNotificationIdentifier.TYPE_PERSON) { mInfoItem = createPartialConversationItem(mContext); } else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 3370773df807..60883f04c9e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -117,6 +117,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -129,8 +131,6 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; -import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; @@ -233,7 +233,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final StackScrollAlgorithm mStackScrollAlgorithm; private final AmbientState mAmbientState; - private NotificationGroupManager mGroupManager; + private GroupMembershipManager mGroupMembershipManager; + private GroupExpansionManager mGroupExpansionManager; private NotificationActivityStarter mNotificationActivityStarter; private HashSet<ExpandableView> mChildrenToAddAnimated = new HashSet<>(); private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>(); @@ -415,7 +416,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable }; private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC); private boolean mPulsing; - private boolean mGroupExpandedForMeasure; private boolean mScrollable; private View mForcedScroll; @@ -560,7 +560,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable NotifPipeline notifPipeline, NotificationEntryManager entryManager, NotifCollection notifCollection, - UiEventLogger uiEventLogger + UiEventLogger uiEventLogger, + GroupMembershipManager groupMembershipManager, + GroupExpansionManager groupExpansionManager ) { super(context, attrs, 0, 0); Resources res = getResources(); @@ -636,6 +638,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } }); } + mGroupMembershipManager = groupMembershipManager; + mGroupExpansionManager = groupExpansionManager; mDynamicPrivacyController = dynamicPrivacyController; mStatusbarStateController = statusbarStateController; @@ -1440,7 +1444,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable ExpandableNotificationRow row = mTopHeadsUpEntry.getRow(); if (row.isChildInGroup()) { final NotificationEntry groupSummary = - mGroupManager.getGroupSummary(row.getEntry().getSbn()); + mGroupMembershipManager.getGroupSummary(row.getEntry()); if (groupSummary != null) { row = groupSummary.getRow(); } @@ -1617,9 +1621,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable NotificationEntry entry = row.getEntry(); if (!mIsExpanded && row.isHeadsUp() && row.isPinned() && mTopHeadsUpEntry.getRow() != row - && mGroupManager.getGroupSummary( - mTopHeadsUpEntry.getSbn()) - != entry) { + && mGroupMembershipManager.getGroupSummary(mTopHeadsUpEntry) != entry) { continue; } return row.getViewAtPosition(touchY - childTop); @@ -3017,8 +3019,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.ADAPTER) private boolean isChildInGroup(View child) { return child instanceof ExpandableNotificationRow - && mGroupManager.isChildInGroupWithSummary( - ((ExpandableNotificationRow) child).getEntry().getSbn()); + && mGroupMembershipManager.isChildInGroup( + ((ExpandableNotificationRow) child).getEntry()); } /** @@ -3083,6 +3085,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return hasAddEvent; } + // TODO (b/162832756): remove since this won't happen in new pipeline (we prune groups in + // ShadeListBuilder) /** * @param child the child to query * @return whether a view is not a top level child but a child notification and that group is @@ -3093,7 +3097,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; NotificationEntry groupSummary = - mGroupManager.getGroupSummary(row.getEntry().getSbn()); + mGroupMembershipManager.getGroupSummary(row.getEntry()); if (groupSummary != null && groupSummary.getRow() != row) { return row.getVisibility() == View.INVISIBLE; } @@ -4349,7 +4353,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (changed) { mWillExpand = false; if (!mIsExpanded) { - mGroupManager.collapseAllGroups(); + mGroupExpansionManager.collapseGroups(); mExpandHelper.cancelImmediately(); } updateNotificationAnimationStates(); @@ -4886,12 +4890,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable this.mStatusBar = statusBar; } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setGroupManager(NotificationGroupManager groupManager) { - this.mGroupManager = groupManager; - mGroupManager.addOnGroupChangeListener(mOnGroupChangeListener); - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) void requestAnimateEverything() { if (mIsExpanded && mAnimationsEnabled) { @@ -4990,17 +4988,29 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable public void removeContainerView(View v) { Assert.isMainThread(); removeView(v); + if (v instanceof ExpandableNotificationRow && !mController.isShowingEmptyShadeView()) { + mController.updateShowEmptyShadeView(); + updateFooter(); + } } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void addContainerView(View v) { Assert.isMainThread(); addView(v); + if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) { + mController.updateShowEmptyShadeView(); + updateFooter(); + } } public void addContainerViewAt(View v, int index) { Assert.isMainThread(); addView(v, index); + if (v instanceof ExpandableNotificationRow && mController.isShowingEmptyShadeView()) { + mController.updateShowEmptyShadeView(); + updateFooter(); + } } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) @@ -5101,6 +5111,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable updateScrollability(); } + boolean isQsExpanded() { + return mQsExpanded; + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setQsExpansionFraction(float qsExpansionFraction) { mQsExpansionFraction = qsExpansionFraction; @@ -6296,39 +6310,22 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable public HeadsUpTouchHelper.Callback getHeadsUpCallback() { return mHeadsUpCallback; } - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() { - @Override - public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) { - boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled - && (mIsExpanded || changedRow.isPinned()); - if (animated) { - mExpandedGroupView = changedRow; - mNeedsAnimation = true; - } - changedRow.setChildrenExpanded(expanded, animated); - if (!mGroupExpandedForMeasure) { - onChildHeightChanged(changedRow, false /* needsAnimation */); - } - runAfterAnimationFinished(new Runnable() { - @Override - public void run() { - changedRow.onFinishedExpansionChange(); - } - }); - } - - @Override - public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) { - mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren"); + void onGroupExpandChanged(ExpandableNotificationRow changedRow, boolean expanded) { + boolean animated = mAnimationsEnabled && (mIsExpanded || changedRow.isPinned()); + if (animated) { + mExpandedGroupView = changedRow; + mNeedsAnimation = true; } + changedRow.setChildrenExpanded(expanded, animated); + onChildHeightChanged(changedRow, false /* needsAnimation */); - @Override - public void onGroupsChanged() { - mStatusBar.requestNotificationUpdate("onGroupsChanged"); - } - }; + runAfterAnimationFinished(new Runnable() { + @Override + public void run() { + changedRow.onFinishedExpansionChange(); + } + }); + } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 70892e0f9b38..a0b49aca6ded 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import android.content.res.Resources; import android.graphics.Point; @@ -56,6 +57,10 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener; +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -67,7 +72,6 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBar; @@ -117,6 +121,8 @@ public class NotificationStackScrollLayoutController { private NotificationStackScrollLayout mView; private boolean mFadeNotificationsOnDismiss; private NotificationSwipeHelper mSwipeHelper; + private boolean mShowEmptyShadeView; + private int mBarState; private final NotificationListContainerImpl mNotificationListContainer = new NotificationListContainerImpl(); @@ -127,6 +133,8 @@ public class NotificationStackScrollLayoutController { @Override public void onViewAttachedToWindow(View v) { mConfigurationController.addCallback(mConfigurationListener); + mZenModeController.addCallback(mZenModeControllerCallback); + mBarState = mStatusBarStateController.getState(); mStatusBarStateController.addCallback( mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); } @@ -134,6 +142,7 @@ public class NotificationStackScrollLayoutController { @Override public void onViewDetachedFromWindow(View v) { mConfigurationController.removeCallback(mConfigurationListener); + mZenModeController.removeCallback(mZenModeControllerCallback); mStatusBarStateController.removeCallback(mStateListener); } }; @@ -154,11 +163,13 @@ public class NotificationStackScrollLayoutController { final ConfigurationListener mConfigurationListener = new ConfigurationListener() { @Override public void onDensityOrFontScaleChanged() { + updateShowEmptyShadeView(); mView.reinflateViews(); } @Override public void onOverlayChanged() { + updateShowEmptyShadeView(); mView.updateCornerRadius(); mView.reinflateViews(); } @@ -179,14 +190,15 @@ public class NotificationStackScrollLayoutController { @Override public void onStatePreChange(int oldState, int newState) { if (oldState == StatusBarState.SHADE_LOCKED - && newState == StatusBarState.KEYGUARD) { + && newState == KEYGUARD) { mView.requestAnimateEverything(); } } @Override public void onStateChanged(int newState) { - mView.setStatusBarState(newState); + mBarState = newState; + mView.setStatusBarState(mBarState); } @Override @@ -480,6 +492,14 @@ public class NotificationStackScrollLayoutController { } }; + private final ZenModeController.Callback mZenModeControllerCallback = + new ZenModeController.Callback() { + @Override + public void onZenChanged(int zen) { + updateShowEmptyShadeView(); + } + }; + @Inject public NotificationStackScrollLayoutController( @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, @@ -501,7 +521,9 @@ public class NotificationStackScrollLayoutController { @Main Resources resources, NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, StatusBar statusBar, - ScrimController scrimController) { + ScrimController scrimController, + NotificationGroupManagerLegacy legacyGroupManager, + GroupExpansionManager groupManager) { mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; @@ -522,12 +544,28 @@ public class NotificationStackScrollLayoutController { mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder; mStatusBar = statusBar; mScrimController = scrimController; + groupManager.registerGroupExpansionChangeListener((changedRow, expanded) -> { + mView.onGroupExpandChanged(changedRow, expanded); + }); + + legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() { + @Override + public void onGroupCreatedFromChildren(NotificationGroup group) { + mStatusBar.requestNotificationUpdate("onGroupCreatedFromChildren"); + } + + @Override + public void onGroupsChanged() { + mStatusBar.requestNotificationUpdate("onGroupsChanged"); + } + }); } public void attach(NotificationStackScrollLayout view) { mView = view; mView.setController(this); mView.setTouchHandler(new TouchHandler()); + mView.setStatusBar(mStatusBar); mSwipeHelper = mNotificationSwipeHelperBuilder .setSwipeDirection(SwipeHelper.X) @@ -795,6 +833,7 @@ public class NotificationStackScrollLayoutController { public void setQsExpanded(boolean expanded) { mView.setQsExpanded(expanded); + updateShowEmptyShadeView(); } public void setScrollingEnabled(boolean enabled) { @@ -903,8 +942,21 @@ public class NotificationStackScrollLayoutController { return mView.getFooterViewHeightWithPadding(); } - public void updateEmptyShadeView(boolean visible) { - mView.updateEmptyShadeView(visible, mZenModeController.areNotificationsHiddenInShade()); + /** + * Update whether we should show the empty shade view (no notifications in the shade). + * If so, send the update to our view. + */ + public void updateShowEmptyShadeView() { + mShowEmptyShadeView = mBarState != KEYGUARD + && !mView.isQsExpanded() + && mView.getVisibleNotificationCount() == 0; + mView.updateEmptyShadeView( + mShowEmptyShadeView, + mZenModeController.areNotificationsHiddenInShade()); + } + + public boolean isShowingEmptyShadeView() { + return mShowEmptyShadeView; } public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { @@ -1012,14 +1064,6 @@ public class NotificationStackScrollLayoutController { mView.setNotificationPanelController(notificationPanelViewController); } - public void setStatusBar(StatusBar statusBar) { - mView.setStatusBar(statusBar); - } - - public void setGroupManager(NotificationGroupManager groupManager) { - mView.setGroupManager(groupManager); - } - public void setShelfController(NotificationShelfController notificationShelfController) { mView.setShelfController(notificationShelfController); } @@ -1095,6 +1139,7 @@ public class NotificationStackScrollLayoutController { } private class NotificationListContainerImpl implements NotificationListContainer { + @Override public void setChildTransferInProgress(boolean childTransferInProgress) { mView.setChildTransferInProgress(childTransferInProgress); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 8092cb910b07..3827123f0160 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -33,6 +33,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController.StateList import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -55,7 +56,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, @VisibleForTesting final int mExtensionTime; private final KeyguardBypassController mBypassController; - private final NotificationGroupManager mGroupManager; + private final GroupMembershipManager mGroupMembershipManager; private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>(); private final int mAutoHeadsUpNotificationDecay; // TODO (b/162832756): remove visual stability manager when migrating to new pipeline @@ -101,7 +102,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, public HeadsUpManagerPhone(@NonNull final Context context, StatusBarStateController statusBarStateController, KeyguardBypassController bypassController, - NotificationGroupManager groupManager, + GroupMembershipManager groupMembershipManager, ConfigurationController configurationController) { super(context); Resources resources = mContext.getResources(); @@ -110,7 +111,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, R.integer.auto_heads_up_notification_decay); statusBarStateController.addCallback(mStatusBarStateListener); mBypassController = bypassController; - mGroupManager = groupManager; + mGroupMembershipManager = groupMembershipManager; updateResources(); configurationController.addCallback(new ConfigurationController.ConfigurationListener() { @@ -166,7 +167,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } else { if (topEntry.isChildInGroup()) { final NotificationEntry groupSummary = - mGroupManager.getGroupSummary(topEntry.getSbn()); + mGroupMembershipManager.getGroupSummary(topEntry); if (groupSummary != null) { topEntry = groupSummary; } 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 b6a284c5e3c4..09034c0899f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -40,6 +40,8 @@ import com.android.keyguard.KeyguardSecurityView; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; +import com.android.keyguard.dagger.ContainerView; +import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.systemui.DejankUtils; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -50,9 +52,12 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.PrintWriter; +import javax.inject.Inject; + /** * A class which manages the bouncer on the lockscreen. */ +@KeyguardBouncerScope public class KeyguardBouncer { private static final String TAG = "KeyguardBouncer"; @@ -95,8 +100,9 @@ public class KeyguardBouncer { private boolean mIsAnimatingAway; private boolean mIsScrimmed; + @Inject public KeyguardBouncer(Context context, ViewMediatorCallback callback, - LockPatternUtils lockPatternUtils, ViewGroup container, + LockPatternUtils lockPatternUtils, @ContainerView ViewGroup container, DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager, BouncerExpansionCallback expansionCallback, KeyguardStateController keyguardStateController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java index dd9c8207af06..3181f520dca2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java @@ -31,11 +31,11 @@ import com.android.systemui.statusbar.AlertingNotificationManager; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.NotificationGroup; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; import com.android.systemui.statusbar.notification.row.RowContentBindParams; import com.android.systemui.statusbar.notification.row.RowContentBindStage; -import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup; -import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; @@ -44,8 +44,8 @@ import java.util.ArrayList; import java.util.Objects; /** - * A helper class dealing with the alert interactions between {@link NotificationGroupManager} and - * {@link HeadsUpManager}. In particular, this class deals with keeping + * A helper class dealing with the alert interactions between {@link NotificationGroupManagerLegacy} + * and {@link HeadsUpManager}. In particular, this class deals with keeping * the correct notification in a group alerting based off the group suppression. */ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener, @@ -66,8 +66,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis private HeadsUpManager mHeadsUpManager; private final RowContentBindStage mRowContentBindStage; - private final NotificationGroupManager mGroupManager = - Dependency.get(NotificationGroupManager.class); + private final NotificationGroupManagerLegacy mGroupManager = + Dependency.get(NotificationGroupManagerLegacy.class); private NotificationEntryManager mEntryManager; @@ -83,7 +83,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis /** Causes the TransferHelper to register itself as a listener to the appropriate classes. */ public void bind(NotificationEntryManager entryManager, - NotificationGroupManager groupManager) { + NotificationGroupManagerLegacy groupManager) { if (mEntryManager != null) { throw new IllegalStateException("Already bound."); } @@ -95,7 +95,7 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis mEntryManager = entryManager; mEntryManager.addNotificationEntryListener(mNotificationEntryListener); - groupManager.addOnGroupChangeListener(mOnGroupChangeListener); + groupManager.registerGroupChangeListener(mOnGroupChangeListener); } /** @@ -128,7 +128,8 @@ public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedLis mIsDozing = isDozing; } - private final OnGroupChangeListener mOnGroupChangeListener = new OnGroupChangeListener() { + private final NotificationGroupManagerLegacy.OnGroupChangeListener mOnGroupChangeListener = + new NotificationGroupManagerLegacy.OnGroupChangeListener() { @Override public void onGroupCreated(NotificationGroup group, String groupKey) { mGroupAlertEntries.put(groupKey, new GroupAlertEntry(group)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 1cd85e3b3cc1..169058a3da21 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -107,6 +107,8 @@ import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; +import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -120,7 +122,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.InjectionInflationController; import java.io.FileDescriptor; @@ -168,9 +169,6 @@ public class NotificationPanelViewController extends PanelViewController { mOnHeadsUpChangedListener = new MyOnHeadsUpChangedListener(); private final HeightListener mHeightListener = new HeightListener(); - private final ZenModeControllerCallback - mZenModeControllerCallback = - new ZenModeControllerCallback(); private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener(); private final ExpansionCallback mExpansionCallback = new ExpansionCallback(); @@ -178,7 +176,6 @@ public class NotificationPanelViewController extends PanelViewController { private final NotificationPanelView mView; private final MetricsLogger mMetricsLogger; private final ActivityManager mActivityManager; - private final ZenModeController mZenModeController; private final ConfigurationController mConfigurationController; private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; @@ -342,8 +339,6 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mKeyguardStatusViewAnimating; private ValueAnimator mQsSizeChangeAnimator; - private boolean mShowEmptyShadeView; - private boolean mQsScrimEnabled = true; private boolean mQsTouchAboveFalsingThreshold; private int mQsFalsingThreshold; @@ -367,7 +362,8 @@ public class NotificationPanelViewController extends PanelViewController { setHeadsUpAnimatingAway(false); notifyBarPanelExpansionChanged(); }; - private NotificationGroupManager mGroupManager; + // TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager + private NotificationGroupManagerLegacy mGroupManager; private boolean mShowIconsWhenExpanded; private int mIndicationBottomPadding; private int mAmbientIndicationBottomPadding; @@ -505,7 +501,7 @@ public class NotificationPanelViewController extends PanelViewController { LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, - ActivityManager activityManager, ZenModeController zenModeController, + ActivityManager activityManager, ConfigurationController configurationController, FlingAnimationUtils.Builder flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, @@ -514,20 +510,21 @@ public class NotificationPanelViewController extends PanelViewController { BiometricUnlockController biometricUnlockController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, - NotificationIconAreaController notificationIconAreaController, - KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) { + KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, + NotificationGroupManagerLegacy groupManager, + NotificationIconAreaController notificationIconAreaController) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); mView = view; mMetricsLogger = metricsLogger; mActivityManager = activityManager; - mZenModeController = zenModeController; mConfigurationController = configurationController; mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder; mMediaHierarchyManager = mediaHierarchyManager; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; + mGroupManager = groupManager; mNotificationIconAreaController = notificationIconAreaController; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mView.setWillNotDraw(!DEBUG); @@ -724,8 +721,6 @@ public class NotificationPanelViewController extends PanelViewController { } private void reInflateViews() { - updateShowEmptyShadeView(); - // Re-inflate the status view group. int index = mView.indexOfChild(mKeyguardStatusView); mView.removeView(mKeyguardStatusView); @@ -1727,7 +1722,6 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationStackScrollLayoutController.setScrollingEnabled( mBarState != KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll)); - updateEmptyShadeView(); mQsNavbarScrim.setVisibility( mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling @@ -2145,7 +2139,7 @@ public class NotificationPanelViewController extends PanelViewController { // it in expanded QS state as well so we don't run into troubles when fading the view in/out // and expanding/collapsing the whole panel from/to quick settings. if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0 - && mShowEmptyShadeView) { + && mNotificationStackScrollLayoutController.isShowingEmptyShadeView()) { notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight(); } int maxQsHeight = mQsMaxExpansionHeight; @@ -2561,17 +2555,6 @@ public class NotificationPanelViewController extends PanelViewController { return mDozing; } - public void showEmptyShadeView(boolean emptyShadeViewVisible) { - mShowEmptyShadeView = emptyShadeViewVisible; - updateEmptyShadeView(); - } - - private void updateEmptyShadeView() { - // Hide "No notifications" in QS. - mNotificationStackScrollLayoutController.updateEmptyShadeView( - mShowEmptyShadeView && !mQsExpanded); - } - public void setQsScrimEnabled(boolean qsScrimEnabled) { boolean changed = mQsScrimEnabled != qsScrimEnabled; mQsScrimEnabled = qsScrimEnabled; @@ -2873,10 +2856,6 @@ public class NotificationPanelViewController extends PanelViewController { return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName()); } - private void setGroupManager(NotificationGroupManager groupManager) { - mGroupManager = groupManager; - } - public boolean hideStatusBarIconsWhenExpanded() { if (mLaunchingNotification) { return mHideIconsDuringNotificationLaunch; @@ -3078,22 +3057,21 @@ public class NotificationPanelViewController extends PanelViewController { return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL); } - private void updateShowEmptyShadeView() { - boolean - showEmptyShadeView = - mBarState != KEYGUARD && !mEntryManager.hasActiveNotifications(); - showEmptyShadeView(showEmptyShadeView); - } - public RemoteInputController.Delegate createRemoteInputDelegate() { return mNotificationStackScrollLayoutController.createDelegate(); } - void updateNotificationViews(String reason) { + /** + * Updates the notification views' sections and status bar icons. This is + * triggered by the NotificationPresenter whenever there are changes to the underlying + * notification data being displayed. In the new notification pipeline, this is handled in + * {@link ShadeViewManager}. + */ + public void updateNotificationViews(String reason) { mNotificationStackScrollLayoutController.updateSectionBoundaries(reason); mNotificationStackScrollLayoutController.updateSpeedBumpIndex(); mNotificationStackScrollLayoutController.updateFooter(); - updateShowEmptyShadeView(); + mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList()); } @@ -3139,15 +3117,10 @@ public class NotificationPanelViewController extends PanelViewController { */ public void initDependencies( StatusBar statusBar, - NotificationGroupManager groupManager, NotificationShelfController notificationShelfController) { setStatusBar(statusBar); - setGroupManager(mGroupManager); mNotificationStackScrollLayoutController.setNotificationPanelController(this); - mNotificationStackScrollLayoutController.setStatusBar(statusBar); - mNotificationStackScrollLayoutController.setGroupManager(groupManager); mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); - updateShowEmptyShadeView(); mNotificationShelfController = notificationShelfController; updateMaxDisplayedNotifications(true); } @@ -3602,20 +3575,8 @@ public class NotificationPanelViewController extends PanelViewController { } } - private class ZenModeControllerCallback implements ZenModeController.Callback { - @Override - public void onZenChanged(int zen) { - updateShowEmptyShadeView(); - } - } - private class ConfigurationListener implements ConfigurationController.ConfigurationListener { @Override - public void onDensityOrFontScaleChanged() { - updateShowEmptyShadeView(); - } - - @Override public void onThemeChanged() { final int themeResId = mView.getContext().getThemeResId(); if (mThemeResId == themeResId) { @@ -3712,7 +3673,6 @@ public class NotificationPanelViewController extends PanelViewController { public void onViewAttachedToWindow(View v) { FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.addCallback(mStatusBarStateListener); - mZenModeController.addCallback(mZenModeControllerCallback); mConfigurationController.addCallback(mConfigurationListener); mUpdateMonitor.registerCallback(mKeyguardUpdateCallback); // Theme might have changed between inflating this view and attaching it to the @@ -3725,7 +3685,6 @@ public class NotificationPanelViewController extends PanelViewController { public void onViewDetachedFromWindow(View v) { FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.removeCallback(mStatusBarStateListener); - mZenModeController.removeCallback(mZenModeControllerCallback); mConfigurationController.removeCallback(mConfigurationListener); mUpdateMonitor.removeCallback(mKeyguardUpdateCallback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 58828797cfde..298672769b56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -699,7 +699,6 @@ public class StatusBar extends SystemUI implements DemoMode, SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper, BubbleController bubbleController, - NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, NavigationBarController navigationBarController, @@ -780,7 +779,6 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarStateController = statusBarStateController; mVibratorHelper = vibratorHelper; mBubbleController = bubbleController; - mGroupManager = groupManager; mVisualStabilityManager = visualStabilityManager; mDeviceProvisionedController = deviceProvisionedController; mNavigationBarController = navigationBarController; @@ -1159,7 +1157,6 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanelViewController.initDependencies( this, - mGroupManager, mNotificationShelfController); BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop); @@ -1506,9 +1503,8 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarKeyguardViewManager.registerStatusBar( /* statusBar= */ this, getBouncerContainer(), mNotificationPanelViewController, mBiometricUnlockController, - mDismissCallbackRegistry, mNotificationShadeWindowView.findViewById(R.id.lock_icon_container), - mStackScroller, mKeyguardBypassController, mFalsingManager); + mStackScroller, mKeyguardBypassController); mKeyguardIndicationController .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); @@ -4084,8 +4080,6 @@ public class StatusBar extends SystemUI implements DemoMode, // all notifications protected NotificationStackScrollLayout mStackScroller; - private final NotificationGroupManager mGroupManager; - // handling reordering private final VisualStabilityManager mVisualStabilityManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 777bf3f73480..b56993b5f439 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -44,15 +44,13 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.systemui.DejankUtils; -import com.android.systemui.SystemUIFactory; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; -import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.FaceAuthScreenBrightnessController; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; @@ -106,6 +104,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final NavigationModeController mNavigationModeController; private final NotificationShadeWindowController mNotificationShadeWindowController; private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController; + private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() { @Override public void onFullyShown() { @@ -216,7 +215,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb NotificationShadeWindowController notificationShadeWindowController, KeyguardStateController keyguardStateController, Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController, - NotificationMediaManager notificationMediaManager) { + NotificationMediaManager notificationMediaManager, + KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) { mContext = context; mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -229,6 +229,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mStatusBarStateController = sysuiStatusBarStateController; mDockManager = dockManager; mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController; + mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory; } @Override @@ -236,9 +237,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb ViewGroup container, NotificationPanelViewController notificationPanelViewController, BiometricUnlockController biometricUnlockController, - DismissCallbackRegistry dismissCallbackRegistry, ViewGroup lockIconContainer, View notificationContainer, - KeyguardBypassController bypassController, FalsingManager falsingManager) { + KeyguardBypassController bypassController) { mStatusBar = statusBar; mContainer = container; mLockIconContainer = lockIconContainer; @@ -246,9 +246,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mLastLockVisible = mLockIconContainer.getVisibility() == View.VISIBLE; } mBiometricUnlockController = biometricUnlockController; - mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, - mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry, - mExpansionCallback, mKeyguardStateController, falsingManager, bypassController); + mBouncer = mKeyguardBouncerComponentFactory + .build(container, mExpansionCallback) + .createKeyguardBouncer(); mNotificationPanelViewController = notificationPanelViewController; notificationPanelViewController.addExpansionListener(this); mBypassController = bypassController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index de11c9023200..f80656706f37 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -66,10 +66,10 @@ import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -96,7 +96,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final NotificationEntryManager mEntryManager; private final NotifPipeline mNotifPipeline; - private final NotifCollection mNotifCollection; private final HeadsUpManagerPhone mHeadsUpManager; private final ActivityStarter mActivityStarter; private final NotificationClickNotifier mClickNotifier; @@ -107,7 +106,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final BubbleController mBubbleController; private final Lazy<AssistManager> mAssistManagerLazy; private final NotificationRemoteInputManager mRemoteInputManager; - private final NotificationGroupManager mGroupManager; + private final GroupMembershipManager mGroupMembershipManager; private final NotificationLockscreenUserManager mLockscreenUserManager; private final ShadeController mShadeController; private final KeyguardStateController mKeyguardStateController; @@ -135,7 +134,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit Executor uiBgExecutor, NotificationEntryManager entryManager, NotifPipeline notifPipeline, - NotifCollection notifCollection, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, NotificationClickNotifier clickNotifier, @@ -146,7 +144,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit BubbleController bubbleController, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, - NotificationGroupManager groupManager, + GroupMembershipManager groupMembershipManager, NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, KeyguardStateController keyguardStateController, @@ -170,7 +168,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mUiBgExecutor = uiBgExecutor; mEntryManager = entryManager; mNotifPipeline = notifPipeline; - mNotifCollection = notifCollection; mHeadsUpManager = headsUpManager; mActivityStarter = activityStarter; mClickNotifier = clickNotifier; @@ -181,7 +178,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mBubbleController = bubbleController; mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; - mGroupManager = groupManager; + mGroupMembershipManager = groupMembershipManager; mLockscreenUserManager = lockscreenUserManager; mShadeController = shadeController; mKeyguardStateController = keyguardStateController; @@ -228,8 +225,9 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) { mLogger.logStartingActivityFromClick(sbn.getKey()); + final NotificationEntry entry = row.getEntry(); RemoteInputController controller = mRemoteInputManager.getController(); - if (controller.isRemoteInputActive(row.getEntry()) + if (controller.isRemoteInputActive(entry) && !TextUtils.isEmpty(row.getActiveRemoteInputText())) { // We have an active remote input typed and the user clicked on the notification. // this was probably unintentional, so we're closing the edit text instead. @@ -240,7 +238,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit final PendingIntent intent = notification.contentIntent != null ? notification.contentIntent : notification.fullScreenIntent; - final boolean isBubble = row.getEntry().isBubble(); + final boolean isBubble = entry.isBubble(); // This code path is now executed for notification without a contentIntent. // The only valid case is Bubble notifications. Guard against other cases @@ -260,7 +258,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mLockscreenUserManager.getCurrentUserId()); ActivityStarter.OnDismissAction postKeyguardAction = () -> handleNotificationClickAfterKeyguardDismissed( - sbn, row, controller, intent, + entry, row, controller, intent, isActivityIntent, wasOccluded, showOverLockscreen); if (showOverLockscreen) { mIsCollapsingToShowActivityOverLockscreen = true; @@ -272,27 +270,27 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } private boolean handleNotificationClickAfterKeyguardDismissed( - StatusBarNotification sbn, + NotificationEntry entry, ExpandableNotificationRow row, RemoteInputController controller, PendingIntent intent, boolean isActivityIntent, boolean wasOccluded, boolean showOverLockscreen) { - mLogger.logHandleClickAfterKeyguardDismissed(sbn.getKey()); + mLogger.logHandleClickAfterKeyguardDismissed(entry.getKey()); // TODO: Some of this code may be able to move to NotificationEntryManager. removeHUN(row); NotificationEntry parentToCancel = null; - if (shouldAutoCancel(sbn) && mGroupManager.isOnlyChildInGroup(sbn)) { - NotificationEntry summarySbn = mGroupManager.getLogicalGroupSummary(sbn); + if (shouldAutoCancel(entry.getSbn()) && mGroupMembershipManager.isOnlyChildInGroup(entry)) { + NotificationEntry summarySbn = mGroupMembershipManager.getLogicalGroupSummary(entry); if (shouldAutoCancel(summarySbn.getSbn())) { parentToCancel = summarySbn; } } final NotificationEntry parentToCancelFinal = parentToCancel; final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed( - sbn, row, controller, intent, + entry, row, controller, intent, isActivityIntent, wasOccluded, parentToCancelFinal); if (showOverLockscreen) { @@ -309,16 +307,16 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } private void handleNotificationClickAfterPanelCollapsed( - StatusBarNotification sbn, + NotificationEntry entry, ExpandableNotificationRow row, RemoteInputController controller, PendingIntent intent, boolean isActivityIntent, boolean wasOccluded, NotificationEntry parentToCancelFinal) { - mLogger.logHandleClickAfterPanelCollapsed(sbn.getKey()); + String notificationKey = entry.getKey(); + mLogger.logHandleClickAfterPanelCollapsed(notificationKey); - String notificationKey = sbn.getKey(); try { // The intent we are sending is for the application, which // won't have permission to immediately start an activity after @@ -346,7 +344,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit } } Intent fillInIntent = null; - NotificationEntry entry = row.getEntry(); CharSequence remoteInputText = null; if (!TextUtils.isEmpty(entry.remoteInputText)) { remoteInputText = entry.remoteInputText; @@ -385,7 +382,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit // necessary in the new pipeline due to group pruning in ShadeListBuilder. removeNotification(parentToCancelFinal); } - if (shouldAutoCancel(sbn) + if (shouldAutoCancel(entry.getSbn()) || mRemoteInputManager.isNotificationKeptForRemoteInputHistory( notificationKey)) { // Automatically remove all notifications that we may have kept around longer @@ -605,7 +602,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final Executor mUiBgExecutor; private final NotificationEntryManager mEntryManager; private final NotifPipeline mNotifPipeline; - private final NotifCollection mNotifCollection; private final HeadsUpManagerPhone mHeadsUpManager; private final ActivityStarter mActivityStarter; private final NotificationClickNotifier mClickNotifier; @@ -616,7 +612,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit private final BubbleController mBubbleController; private final Lazy<AssistManager> mAssistManagerLazy; private final NotificationRemoteInputManager mRemoteInputManager; - private final NotificationGroupManager mGroupManager; + private final GroupMembershipManager mGroupMembershipManager; private final NotificationLockscreenUserManager mLockscreenUserManager; private final ShadeController mShadeController; private final KeyguardStateController mKeyguardStateController; @@ -643,7 +639,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit @UiBackground Executor uiBgExecutor, NotificationEntryManager entryManager, NotifPipeline notifPipeline, - NotifCollection notifCollection, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, NotificationClickNotifier clickNotifier, @@ -654,7 +649,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit BubbleController bubbleController, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, - NotificationGroupManager groupManager, + GroupMembershipManager groupMembershipManager, NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, KeyguardStateController keyguardStateController, @@ -674,7 +669,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mUiBgExecutor = uiBgExecutor; mEntryManager = entryManager; mNotifPipeline = notifPipeline; - mNotifCollection = notifCollection; mHeadsUpManager = headsUpManager; mActivityStarter = activityStarter; mClickNotifier = clickNotifier; @@ -685,7 +679,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mBubbleController = bubbleController; mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; - mGroupManager = groupManager; + mGroupMembershipManager = groupMembershipManager; mLockscreenUserManager = lockscreenUserManager; mShadeController = shadeController; mKeyguardStateController = keyguardStateController; @@ -731,7 +725,6 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mUiBgExecutor, mEntryManager, mNotifPipeline, - mNotifCollection, mHeadsUpManager, mActivityStarter, mClickNotifier, @@ -742,7 +735,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit mBubbleController, mAssistManagerLazy, mRemoteInputManager, - mGroupManager, + mGroupMembershipManager, mLockscreenUserManager, mShadeController, mKeyguardStateController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 8a8942975d2e..36519ac0d808 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -45,6 +45,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoteInputManager.Callback; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -65,7 +66,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final ShadeController mShadeController; private final ActivityIntentHelper mActivityIntentHelper; - private final NotificationGroupManager mGroupManager; + private final GroupExpansionManager mGroupExpansionManager; private View mPendingWorkRemoteInputView; private View mPendingRemoteInputView; private KeyguardManager mKeyguardManager; @@ -78,12 +79,15 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, /** */ @Inject - public StatusBarRemoteInputCallback(Context context, NotificationGroupManager groupManager, + public StatusBarRemoteInputCallback( + Context context, + GroupExpansionManager groupExpansionManager, NotificationLockscreenUserManager notificationLockscreenUserManager, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, - ActivityStarter activityStarter, ShadeController shadeController, + ActivityStarter activityStarter, + ShadeController shadeController, CommandQueue commandQueue, ActionClickLogger clickLogger) { mContext = context; @@ -101,7 +105,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, mCommandQueue.addCallback(this); mActionClickLogger = clickLogger; mActivityIntentHelper = new ActivityIntentHelper(mContext); - mGroupManager = groupManager; + mGroupExpansionManager = groupExpansionManager; } @Override @@ -182,7 +186,7 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, } else { if (row.isChildInGroup() && !row.areChildrenExpanded()) { // The group isn't expanded, let's make sure it's visible! - mGroupManager.toggleGroupExpansion(row.getEntry().getSbn()); + mGroupExpansionManager.toggleGroupExpansion(row.getEntry()); } row.setUserExpanded(true); row.getPrivateLayout().setOnExpandedVisibleListener(clickedView::performClick); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index 72067d376f7c..16c3dc460a9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -80,7 +80,6 @@ import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LightsOutNotifController; import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.LockscreenWallpaper; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.PhoneStatusBarPolicy; import com.android.systemui.statusbar.phone.ScrimController; @@ -158,7 +157,6 @@ public interface StatusBarPhoneModule { SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper, BubbleController bubbleController, - NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, NavigationBarController navigationBarController, @@ -238,7 +236,6 @@ public interface StatusBarPhoneModule { statusBarStateController, vibratorHelper, bubbleController, - groupManager, visualStabilityManager, deviceProvisionedController, navigationBarController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java index 9b4e16525df2..485b1b109eb4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java @@ -15,8 +15,6 @@ package com.android.systemui.statusbar.policy; import android.app.ActivityManager; -import android.content.ContentResolver; -import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; @@ -30,6 +28,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.CurrentUserTracker; +import com.android.systemui.util.settings.GlobalSettings; +import com.android.systemui.util.settings.SecureSettings; import java.util.ArrayList; @@ -43,8 +43,8 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen protected static final String TAG = DeviceProvisionedControllerImpl.class.getSimpleName(); protected final ArrayList<DeviceProvisionedListener> mListeners = new ArrayList<>(); - private final ContentResolver mContentResolver; - private final Context mContext; + private final GlobalSettings mGlobalSettings; + private final SecureSettings mSecureSettings; private final Uri mDeviceProvisionedUri; private final Uri mUserSetupUri; protected final ContentObserver mSettingsObserver; @@ -52,13 +52,14 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen /** */ @Inject - public DeviceProvisionedControllerImpl(Context context, @Main Handler mainHandler, - BroadcastDispatcher broadcastDispatcher) { + public DeviceProvisionedControllerImpl(@Main Handler mainHandler, + BroadcastDispatcher broadcastDispatcher, GlobalSettings globalSettings, + SecureSettings secureSettings) { super(broadcastDispatcher); - mContext = context; - mContentResolver = context.getContentResolver(); - mDeviceProvisionedUri = Global.getUriFor(Global.DEVICE_PROVISIONED); - mUserSetupUri = Secure.getUriFor(Secure.USER_SETUP_COMPLETE); + mGlobalSettings = globalSettings; + mSecureSettings = secureSettings; + mDeviceProvisionedUri = mGlobalSettings.getUriFor(Global.DEVICE_PROVISIONED); + mUserSetupUri = mSecureSettings.getUriFor(Secure.USER_SETUP_COMPLETE); mSettingsObserver = new ContentObserver(mainHandler) { @Override public void onChange(boolean selfChange, Uri uri, int flags) { @@ -74,13 +75,12 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen @Override public boolean isDeviceProvisioned() { - return Global.getInt(mContentResolver, Global.DEVICE_PROVISIONED, 0) != 0; + return mGlobalSettings.getInt(Global.DEVICE_PROVISIONED, 0) != 0; } @Override public boolean isUserSetup(int currentUser) { - return Secure.getIntForUser(mContentResolver, Secure.USER_SETUP_COMPLETE, 0, currentUser) - != 0; + return mSecureSettings.getIntForUser(Secure.USER_SETUP_COMPLETE, 0, currentUser) != 0; } @Override @@ -107,24 +107,24 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen } protected void startListening(int user) { - mContentResolver.registerContentObserver(mDeviceProvisionedUri, true, + mGlobalSettings.registerContentObserverForUser(mDeviceProvisionedUri, true, mSettingsObserver, 0); - mContentResolver.registerContentObserver(mUserSetupUri, true, + mSecureSettings.registerContentObserverForUser(mUserSetupUri, true, mSettingsObserver, user); startTracking(); } protected void stopListening() { stopTracking(); - mContentResolver.unregisterContentObserver(mSettingsObserver); + mGlobalSettings.unregisterContentObserver(mSettingsObserver); } @Override public void onUserSwitched(int newUserId) { - mContentResolver.unregisterContentObserver(mSettingsObserver); - mContentResolver.registerContentObserver(mDeviceProvisionedUri, true, + mGlobalSettings.unregisterContentObserver(mSettingsObserver); + mGlobalSettings.registerContentObserverForUser(mDeviceProvisionedUri, true, mSettingsObserver, 0); - mContentResolver.registerContentObserver(mUserSetupUri, true, + mSecureSettings.registerContentObserverForUser(mUserSetupUri, true, mSettingsObserver, newUserId); notifyUserChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index f9ac760a3367..17fcb1dd6f1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -206,7 +206,7 @@ public class UserSwitcherController implements Dumpable { @Override protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) { final SparseArray<Bitmap> bitmaps = params[0]; - List<UserInfo> infos = mUserManager.getUsers(true); + List<UserInfo> infos = mUserManager.getAliveUsers(); if (infos == null) { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index e7c10f1697f5..d727bfbdf48a 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -47,11 +47,11 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.ShadeControllerImpl; @@ -136,7 +136,7 @@ public abstract class TvSystemUIModule { Context context, StatusBarStateController statusBarStateController, KeyguardBypassController bypassController, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, ConfigurationController configurationController) { return new HeadsUpManagerPhone(context, statusBarStateController, bypassController, groupManager, configurationController); diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java new file mode 100644 index 000000000000..5946af383b0f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.concurrency; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; + +import com.android.systemui.dagger.qualifiers.Main; + +import java.util.concurrent.Executor; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; + +/** + * Dagger Module for classes found within the concurrent package. + */ +@Module +public abstract class GlobalConcurrencyModule { + + /** + * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}. + */ + @Binds + public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl); + + /** Main Looper */ + @Provides + @Main + public static Looper provideMainLooper() { + return Looper.getMainLooper(); + } + + /** + * Main Handler. + * + * Prefer the Main Executor when possible. + */ + @Provides + @Main + public static Handler provideMainHandler(@Main Looper mainLooper) { + return new Handler(mainLooper); + } + + /** + * Provide a Main-Thread Executor. + */ + @Provides + @Main + public static Executor provideMainExecutor(Context context) { + return context.getMainExecutor(); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java index 628c808aa12b..b9b20c73c5d5 100644 --- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java +++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java @@ -16,7 +16,6 @@ package com.android.systemui.util.concurrency; -import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -31,7 +30,6 @@ import com.android.systemui.dagger.qualifiers.UiBackground; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import dagger.Binds; import dagger.Module; import dagger.Provides; @@ -39,7 +37,7 @@ import dagger.Provides; * Dagger Module for classes found within the concurrent package. */ @Module -public abstract class ConcurrencyModule { +public abstract class SysUIConcurrencyModule { /** Background Looper */ @Provides @SysUISingleton @@ -62,13 +60,6 @@ public abstract class ConcurrencyModule { return thread.getLooper(); } - /** Main Looper */ - @Provides - @Main - public static Looper provideMainLooper() { - return Looper.getMainLooper(); - } - /** * Background Handler. * @@ -81,17 +72,6 @@ public abstract class ConcurrencyModule { } /** - * Main Handler. - * - * Prefer the Main Executor when possible. - */ - @Provides - @Main - public static Handler provideMainHandler(@Main Looper mainLooper) { - return new Handler(mainLooper); - } - - /** * Provide a Background-Thread Executor by default. */ @Provides @@ -121,15 +101,6 @@ public abstract class ConcurrencyModule { } /** - * Provide a Main-Thread Executor. - */ - @Provides - @Main - public static Executor provideMainExecutor(Context context) { - return context.getMainExecutor(); - } - - /** * Provide a Background-Thread Executor by default. */ @Provides @@ -199,10 +170,4 @@ public abstract class ConcurrencyModule { public static Executor provideUiBackgroundExecutor() { return Executors.newSingleThreadExecutor(); } - - /** - * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}. - */ - @Binds - public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java index 5c37f797b678..5aaf7f680d5c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.java @@ -67,7 +67,35 @@ public interface SettingsProxy { * Implicitly calls {@link #getUriFor(String)} on the passed in name. */ default void registerContentObserver(String name, ContentObserver settingsObserver) { - registerContentObserverForUser(name, settingsObserver, getUserId()); + registerContentObserver(getUriFor(name), settingsObserver); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' + */ + default void registerContentObserver(Uri uri, ContentObserver settingsObserver) { + registerContentObserverForUser(uri, settingsObserver, getUserId()); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' + * + * Implicitly calls {@link #getUriFor(String)} on the passed in name. + */ + default void registerContentObserver(String name, boolean notifyForDescendents, + ContentObserver settingsObserver) { + registerContentObserver(getUriFor(name), notifyForDescendents, settingsObserver); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.' + */ + default void registerContentObserver(Uri uri, boolean notifyForDescendents, + ContentObserver settingsObserver) { + registerContentObserverForUser(uri, notifyForDescendents, settingsObserver, getUserId()); } /** @@ -78,8 +106,42 @@ public interface SettingsProxy { */ default void registerContentObserverForUser( String name, ContentObserver settingsObserver, int userHandle) { + registerContentObserverForUser( + getUriFor(name), settingsObserver, userHandle); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} + */ + default void registerContentObserverForUser( + Uri uri, ContentObserver settingsObserver, int userHandle) { + registerContentObserverForUser( + uri, false, settingsObserver, userHandle); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} + * + * Implicitly calls {@link #getUriFor(String)} on the passed in name. + */ + default void registerContentObserverForUser( + String name, boolean notifyForDescendents, ContentObserver settingsObserver, + int userHandle) { + registerContentObserverForUser( + getUriFor(name), notifyForDescendents, settingsObserver, userHandle); + } + + /** + * Convenience wrapper around + * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)} + */ + default void registerContentObserverForUser( + Uri uri, boolean notifyForDescendents, ContentObserver settingsObserver, + int userHandle) { getContentResolver().registerContentObserver( - getUriFor(name), false, settingsObserver, userHandle); + uri, notifyForDescendents, settingsObserver, userHandle); } /** See {@link ContentResolver#unregisterContentObserver(ContentObserver)}. */ diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 51ad30ebcac6..78f83d3c09b4 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -49,6 +49,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PixelFormat; +import android.graphics.Region; import android.graphics.drawable.ColorDrawable; import android.media.AudioManager; import android.media.AudioSystem; @@ -71,6 +72,7 @@ import android.view.View.AccessibilityDelegate; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.ViewStub; +import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; @@ -109,7 +111,8 @@ import java.util.List; * Methods ending in "H" must be called on the (ui) handler. */ public class VolumeDialogImpl implements VolumeDialog, - ConfigurationController.ConfigurationListener { + ConfigurationController.ConfigurationListener, + ViewTreeObserver.OnComputeInternalInsetsListener { private static final String TAG = Util.logTag(VolumeDialogImpl.class); private static final long USER_ATTEMPT_GRACE_PERIOD = 1000; @@ -126,6 +129,7 @@ public class VolumeDialogImpl implements VolumeDialog, private final H mHandler = new H(); private final VolumeDialogController mController; private final DeviceProvisionedController mDeviceProvisionedController; + private final Region mTouchableRegion = new Region(); private Window mWindow; private CustomDialog mDialog; @@ -204,6 +208,33 @@ public class VolumeDialogImpl implements VolumeDialog, Dependency.get(ConfigurationController.class).removeCallback(this); } + @Override + public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo internalInsetsInfo) { + // Set touchable region insets on the root dialog view. This tells WindowManager that + // touches outside of this region should not be delivered to the volume window, and instead + // go to the window below. This is the only way to do this - returning false in + // onDispatchTouchEvent results in the event being ignored entirely, rather than passed to + // the next window. + internalInsetsInfo.setTouchableInsets( + ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + + mTouchableRegion.setEmpty(); + + // Set the touchable region to the union of all child view bounds. We don't use touches on + // the volume dialog container itself, so this is fine. + for (int i = 0; i < mDialogView.getChildCount(); i++) { + final View view = mDialogView.getChildAt(i); + mTouchableRegion.op( + view.getLeft(), + view.getTop(), + view.getRight(), + view.getBottom(), + Region.Op.UNION); + } + + internalInsetsInfo.touchableRegion.set(mTouchableRegion); + } + private void initDialog() { mDialog = new CustomDialog(mContext); @@ -235,6 +266,7 @@ public class VolumeDialogImpl implements VolumeDialog, mDialogView.setAlpha(0); mDialog.setCanceledOnTouchOutside(true); mDialog.setOnShowListener(dialog -> { + mDialogView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2.0f); mDialogView.setAlpha(0); mDialogView.animate() @@ -253,6 +285,11 @@ public class VolumeDialogImpl implements VolumeDialog, .start(); }); + mDialog.setOnDismissListener(dialogInterface -> + mDialogView + .getViewTreeObserver() + .removeOnComputeInternalInsetsListener(VolumeDialogImpl.this)); + mDialogView.setOnHoverListener((v, event) -> { int action = event.getActionMasked(); mHovering = (action == MotionEvent.ACTION_HOVER_ENTER) @@ -1369,6 +1406,11 @@ public class VolumeDialogImpl implements VolumeDialog, super(context, R.style.volume_dialog_theme); } + /** + * NOTE: This will only be called for touches within the touchable region of the volume + * dialog, as returned by {@link #onComputeInternalInsets}. Other touches, even if they are + * within the bounds of the volume dialog, will fall through to the window below. + */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { rescheduleTimeoutH(); @@ -1387,6 +1429,12 @@ public class VolumeDialogImpl implements VolumeDialog, mHandler.sendEmptyMessage(H.RECHECK_ALL); } + /** + * NOTE: This will be called with ACTION_OUTSIDE MotionEvents for touches that occur outside + * of the touchable region of the volume dialog (as returned by + * {@link #onComputeInternalInsets}) even if those touches occurred within the bounds of the + * volume dialog. + */ @Override public boolean onTouchEvent(MotionEvent event) { if (mShowing) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index a7808ad54d63..f65c2c91c50a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -77,13 +77,13 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenLockIconController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; @@ -118,7 +118,7 @@ public class BubbleControllerTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; @Mock - private NotificationGroupManager mNotificationGroupManager; + private NotificationGroupManagerLegacy mNotificationGroupManager; @Mock private WindowManager mWindowManager; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java index 4936360756fd..dd191e9cd328 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -75,6 +75,7 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; @@ -82,7 +83,6 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenLockIconController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; @@ -116,7 +116,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Mock private NotificationEntryManager mNotificationEntryManager; @Mock - private NotificationGroupManager mNotificationGroupManager; + private NotificationGroupManagerLegacy mNotificationGroupManager; @Mock private BubbleController.NotifCallback mNotifCallback; @Mock diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java index 51ca2a4e5966..58b27f24a1d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java @@ -30,8 +30,8 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifPipeline; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -52,7 +52,7 @@ public class TestableBubbleController extends BubbleController { NotificationInterruptStateProvider interruptionStateProvider, ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, NotificationEntryManager entryManager, NotifPipeline notifPipeline, FeatureFlags featureFlags, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java index d2bf483a6bd6..d041ee047ae0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java @@ -45,6 +45,7 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -53,7 +54,6 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.google.android.collect.Lists; @@ -76,7 +76,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { // Dependency mocks: @Mock private NotificationEntryManager mEntryManager; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; - @Mock private NotificationGroupManager mGroupManager; + @Mock private NotificationGroupManagerLegacy mGroupManager; @Mock private VisualStabilityManager mVisualStabilityManager; private TestableLooper mTestableLooper; @@ -95,7 +95,7 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager); mDependency.injectTestDependency(NotificationLockscreenUserManager.class, mLockscreenUserManager); - mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager); + mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager); mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager); when(mVisualStabilityManager.areGroupChangesAllowed()).thenReturn(true); when(mVisualStabilityManager.isReorderingAllowed()).thenReturn(true); @@ -136,11 +136,11 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { Lists.newArrayList(entry0, entry1, entry2)); // Set up group manager to report that they should be bundled now. - when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false); - when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(true); - when(mGroupManager.isChildInGroupWithSummary(entry2.getSbn())).thenReturn(true); - when(mGroupManager.getGroupSummary(entry1.getSbn())).thenReturn(entry0); - when(mGroupManager.getGroupSummary(entry2.getSbn())).thenReturn(entry0); + when(mGroupManager.isChildInGroup(entry0)).thenReturn(false); + when(mGroupManager.isChildInGroup(entry1)).thenReturn(true); + when(mGroupManager.isChildInGroup(entry2)).thenReturn(true); + when(mGroupManager.getGroupSummary(entry1)).thenReturn(entry0); + when(mGroupManager.getGroupSummary(entry2)).thenReturn(entry0); // Run updateNotifications - the view hierarchy should be reorganized. mViewHierarchyManager.updateNotificationViews(); @@ -165,9 +165,9 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { Lists.newArrayList(entry0, entry1, entry2)); // Set up group manager to report that they should not be bundled now. - when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false); - when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(false); - when(mGroupManager.isChildInGroupWithSummary(entry2.getSbn())).thenReturn(false); + when(mGroupManager.isChildInGroup(entry0)).thenReturn(false); + when(mGroupManager.isChildInGroup(entry1)).thenReturn(false); + when(mGroupManager.isChildInGroup(entry2)).thenReturn(false); // Run updateNotifications - the view hierarchy should be reorganized. mViewHierarchyManager.updateNotificationViews(); @@ -194,8 +194,8 @@ public class NotificationViewHierarchyManagerTest extends SysuiTestCase { Lists.newArrayList(entry0, entry1)); // Set up group manager to report a suppressed summary now. - when(mGroupManager.isChildInGroupWithSummary(entry0.getSbn())).thenReturn(false); - when(mGroupManager.isChildInGroupWithSummary(entry1.getSbn())).thenReturn(false); + when(mGroupManager.isChildInGroup(entry0)).thenReturn(false); + when(mGroupManager.isChildInGroup(entry1)).thenReturn(false); when(mGroupManager.isSummaryOfSuppressedGroup(entry0.getSbn())).thenReturn(true); // Run updateNotifications - the view hierarchy should be reorganized. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index fc0201ad82f0..3e1616c9fa7b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -75,13 +75,13 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationEntryManagerInflationTest; import com.android.systemui.statusbar.notification.row.RowInflaterTask; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; @@ -119,7 +119,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private NotificationRemoveInterceptor mRemoveInterceptor; @Mock private HeadsUpManager mHeadsUpManager; @Mock private RankingMap mRankingMap; - @Mock private NotificationGroupManager mGroupManager; + @Mock private NotificationGroupManagerLegacy mGroupManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private RowInflaterTask mAsyncInflationTask; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java index 5a81d36ea744..dfe006dfd4fe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java @@ -48,10 +48,10 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.notification.NotificationEntryManager.KeyguardEnvironment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.ShadeController; import org.junit.After; @@ -109,8 +109,8 @@ public class NotificationFilterTest extends SysuiTestCase { eq(UID_ALLOW_DURING_SETUP))) .thenReturn(PackageManager.PERMISSION_GRANTED); mDependency.injectTestDependency(ForegroundServiceController.class, mFsc); - mDependency.injectTestDependency(NotificationGroupManager.class, - new NotificationGroupManager( + mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, + new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class))); mDependency.injectMockDependency(ShadeController.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java index 386c866cdd03..14877eec9a83 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java @@ -37,8 +37,8 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import org.junit.Before; import org.junit.Test; @@ -48,12 +48,13 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) public class HighPriorityProviderTest extends SysuiTestCase { @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier; - @Mock private NotificationGroupManager mGroupManager; + @Mock private GroupMembershipManager mGroupMembershipManager; private HighPriorityProvider mHighPriorityProvider; @Before @@ -61,7 +62,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mHighPriorityProvider = new HighPriorityProvider( mPeopleNotificationIdentifier, - mGroupManager); + mGroupMembershipManager); } @Test @@ -71,7 +72,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setImportance(IMPORTANCE_HIGH) .build(); when(mPeopleNotificationIdentifier - .getPeopleNotificationType(entry.getSbn(), entry.getRanking())) + .getPeopleNotificationType(entry)) .thenReturn(TYPE_NON_PERSON); // THEN it has high priority @@ -88,7 +89,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setImportance(IMPORTANCE_LOW) .build(); when(mPeopleNotificationIdentifier - .getPeopleNotificationType(entry.getSbn(), entry.getRanking())) + .getPeopleNotificationType(entry)) .thenReturn(TYPE_PERSON); // THEN it has high priority @@ -105,7 +106,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setNotification(notification) .build(); when(mPeopleNotificationIdentifier - .getPeopleNotificationType(entry.getSbn(), entry.getRanking())) + .getPeopleNotificationType(entry)) .thenReturn(TYPE_NON_PERSON); // THEN it has high priority @@ -123,7 +124,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setImportance(IMPORTANCE_LOW) .build(); when(mPeopleNotificationIdentifier - .getPeopleNotificationType(entry.getSbn(), entry.getRanking())) + .getPeopleNotificationType(entry)) .thenReturn(TYPE_NON_PERSON); // THEN it has high priority @@ -141,7 +142,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setImportance(IMPORTANCE_MIN) .build(); when(mPeopleNotificationIdentifier - .getPeopleNotificationType(entry.getSbn(), entry.getRanking())) + .getPeopleNotificationType(entry)) .thenReturn(TYPE_NON_PERSON); // THEN it does NOT have high priority @@ -165,7 +166,7 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setChannel(channel) .build(); when(mPeopleNotificationIdentifier - .getPeopleNotificationType(entry.getSbn(), entry.getRanking())) + .getPeopleNotificationType(entry)) .thenReturn(TYPE_PERSON); // THEN it does NOT have high priority @@ -173,13 +174,13 @@ public class HighPriorityProviderTest extends SysuiTestCase { } @Test - public void testIsHighPriority_checkChildrenToCalculatePriority() { + public void testIsHighPriority_checkChildrenToCalculatePriority_legacy() { // GIVEN: a summary with low priority has a highPriorityChild and a lowPriorityChild final NotificationEntry summary = createNotifEntry(false); final NotificationEntry lowPriorityChild = createNotifEntry(false); final NotificationEntry highPriorityChild = createNotifEntry(true); - when(mGroupManager.isGroupSummary(summary.getSbn())).thenReturn(true); - when(mGroupManager.getChildren(summary.getSbn())).thenReturn( + when(mGroupMembershipManager.isGroupSummary(summary)).thenReturn(true); + when(mGroupMembershipManager.getChildren(summary)).thenReturn( new ArrayList<>(Arrays.asList(lowPriorityChild, highPriorityChild))); // THEN the summary is high priority since it has a high priority child @@ -210,16 +211,20 @@ public class HighPriorityProviderTest extends SysuiTestCase { } @Test - public void testIsHighPriority_checkChildrenToCalculatePriorityOf() { + public void testIsHighPriority_checkChildrenToCalculatePriority() { // GIVEN: - // GroupEntry = parentEntry, summary = lowPrioritySummary + // parent with summary = lowPrioritySummary // NotificationEntry = lowPriorityChild // NotificationEntry = highPriorityChild + final NotificationEntry lowPrioritySummary = createNotifEntry(false); final GroupEntry parentEntry = new GroupEntryBuilder() - .setSummary(createNotifEntry(false)) - .addChild(createNotifEntry(false)) - .addChild(createNotifEntry(true)) + .setSummary(lowPrioritySummary) .build(); + when(mGroupMembershipManager.getChildren(parentEntry)).thenReturn( + new ArrayList<>( + List.of( + createNotifEntry(false), + createNotifEntry(true)))); // THEN the GroupEntry parentEntry is high priority since it has a high priority child assertTrue(mHighPriorityProvider.isHighPriority(parentEntry)); @@ -228,13 +233,15 @@ public class HighPriorityProviderTest extends SysuiTestCase { @Test public void testIsHighPriority_childEntryRankingUpdated() { // GIVEN: - // GroupEntry = parentEntry, summary = lowPrioritySummary + // parent with summary = lowPrioritySummary // NotificationEntry = lowPriorityChild - final NotificationEntry lowPriorityChild = createNotifEntry(false); + final NotificationEntry lowPrioritySummary = createNotifEntry(false); final GroupEntry parentEntry = new GroupEntryBuilder() - .setSummary(createNotifEntry(false)) - .addChild(lowPriorityChild) + .setSummary(lowPrioritySummary) .build(); + final NotificationEntry lowPriorityChild = createNotifEntry(false); + when(mGroupMembershipManager.getChildren(parentEntry)).thenReturn( + new ArrayList<>(List.of(lowPriorityChild))); // WHEN the child entry ranking changes to high priority lowPriorityChild.setRanking( @@ -243,9 +250,8 @@ public class HighPriorityProviderTest extends SysuiTestCase { .setImportance(IMPORTANCE_HIGH) .build()); - // THEN the parent entry's high priority value is updated - but not the parent's summary + // THEN the parent entry's high priority value is updated assertTrue(mHighPriorityProvider.isHighPriority(parentEntry)); - assertFalse(mHighPriorityProvider.isHighPriority(parentEntry.getSummary())); } private NotificationEntry createNotifEntry(boolean highPriority) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index 82a7774b4d82..c832fe481f74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -40,7 +40,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT -import com.android.systemui.statusbar.phone.NotificationGroupManager +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.policy.HeadsUpManager import com.google.common.truth.Truth.assertThat import dagger.Lazy @@ -71,14 +71,14 @@ class NotificationRankingManagerTest : SysuiTestCase() { notificationFilter = mock(NotificationFilter::class.java) rankingManager = TestableNotificationRankingManager( lazyMedia, - mock(NotificationGroupManager::class.java), + mock(NotificationGroupManagerLegacy::class.java), mock(HeadsUpManager::class.java), notificationFilter, mock(NotificationEntryManagerLogger::class.java), sectionsManager, personNotificationIdentifier, HighPriorityProvider(personNotificationIdentifier, - mock(NotificationGroupManager::class.java)) + mock(NotificationGroupManagerLegacy::class.java)) ) } @@ -174,7 +174,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setOverrideGroupKey("") .build() - whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking)) + whenever(personNotificationIdentifier.getPeopleNotificationType(a)) .thenReturn(TYPE_IMPORTANT_PERSON) val bN = Notification.Builder(mContext, "test") @@ -194,7 +194,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { whenever(it.isHeadsUp).thenReturn(true) } - whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking)) + whenever(personNotificationIdentifier.getPeopleNotificationType(a)) .thenReturn(TYPE_PERSON) assertEquals(listOf(b, a), rankingManager.updateRanking(null, listOf(a, b), "test")) @@ -216,7 +216,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setUser(mContext.user) .setOverrideGroupKey("") .build() - whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking)) + whenever(personNotificationIdentifier.getPeopleNotificationType(a)) .thenReturn(TYPE_PERSON) val bN = Notification.Builder(mContext, "test") @@ -232,7 +232,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setUser(mContext.user) .setOverrideGroupKey("") .build() - whenever(personNotificationIdentifier.getPeopleNotificationType(b.sbn, b.ranking)) + whenever(personNotificationIdentifier.getPeopleNotificationType(b)) .thenReturn(TYPE_IMPORTANT_PERSON) whenever(personNotificationIdentifier.compareTo(TYPE_PERSON, TYPE_IMPORTANT_PERSON)) @@ -261,7 +261,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setUser(mContext.user) .setOverrideGroupKey("") .build() - whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking)) + whenever(personNotificationIdentifier.getPeopleNotificationType(a)) .thenReturn(TYPE_PERSON) val bN = Notification.Builder(mContext, "test") @@ -277,7 +277,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setUser(mContext.user) .setOverrideGroupKey("") .build() - whenever(personNotificationIdentifier.getPeopleNotificationType(b.sbn, b.ranking)) + whenever(personNotificationIdentifier.getPeopleNotificationType(b)) .thenReturn(TYPE_FULL_PERSON) whenever(personNotificationIdentifier.compareTo(TYPE_PERSON, TYPE_FULL_PERSON)) @@ -400,7 +400,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { .setUser(mContext.user) .setOverrideGroupKey("") .build() - whenever(personNotificationIdentifier.getPeopleNotificationType(a.sbn, a.ranking)) + whenever(personNotificationIdentifier.getPeopleNotificationType(a)) .thenReturn(TYPE_IMPORTANT_PERSON) assertThat(rankingManager.updateRanking(null, listOf(a, b, c), "test")) @@ -410,7 +410,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { internal class TestableNotificationRankingManager( mediaManager: Lazy<NotificationMediaManager>, - groupManager: NotificationGroupManager, + groupManager: NotificationGroupManagerLegacy, headsUpManager: HeadsUpManager, filter: NotificationFilter, logger: NotificationEntryManagerLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt index c49393d2ed34..09c9bcd967bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt @@ -84,8 +84,8 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test fun testInPeopleSection() { - whenever(peopleNotificationIdentifier.getPeopleNotificationType( - entry.sbn, entry.ranking)).thenReturn(TYPE_PERSON) + whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry)) + .thenReturn(TYPE_PERSON) // only put people notifications in this section assertTrue(peopleSectioner.isInSection(entry)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 7a0a19bd5424..aff8ade6f1ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -67,6 +67,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.LowPriorityInflationHelper; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.icon.IconBuilder; import com.android.systemui.statusbar.notification.icon.IconManager; @@ -77,7 +78,6 @@ import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotifica import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.util.concurrency.FakeExecutor; @@ -130,7 +130,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private StatusBarStateController mStatusBarStateController; - @Mock private NotificationGroupManager mGroupManager; + @Mock private NotificationGroupManagerLegacy mGroupMembershipManager; + @Mock private NotificationGroupManagerLegacy mGroupExpansionManager; @Mock private FeatureFlags mFeatureFlags; @Mock private LeakDetector mLeakDetector; @@ -170,10 +171,10 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mEntryManager = new NotificationEntryManager( mock(NotificationEntryManagerLogger.class), - mGroupManager, + mGroupMembershipManager, new NotificationRankingManager( () -> mock(NotificationMediaManager.class), - mGroupManager, + mGroupMembershipManager, mHeadsUpManager, mock(NotificationFilter.class), mock(NotificationEntryManagerLogger.class), @@ -228,7 +229,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { new FakeSystemClock(), "FOOBAR", "FOOBAR", mKeyguardBypassController, - mGroupManager, + mGroupMembershipManager, + mGroupExpansionManager, mRowContentBindStage, mock(NotificationLogger.class), mHeadsUpManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index df26c5b15505..b952c056c33d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -57,6 +57,7 @@ import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.icon.IconBuilder; @@ -68,7 +69,6 @@ import com.android.systemui.statusbar.notification.row.NotificationRowContentBin import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.policy.SmartReplyConstants; import org.mockito.ArgumentCaptor; @@ -96,7 +96,8 @@ public class NotificationTestHelper { private final Context mContext; private final TestableLooper mTestLooper; private int mId; - private final NotificationGroupManager mGroupManager; + private final NotificationGroupManagerLegacy mGroupMembershipManager; + private final NotificationGroupManagerLegacy mGroupExpansionManager; private ExpandableNotificationRow mRow; private HeadsUpManagerPhone mHeadsUpManager; private final NotifBindPipeline mBindPipeline; @@ -116,13 +117,14 @@ public class NotificationTestHelper { dependency.injectMockDependency(BubbleController.class); dependency.injectMockDependency(NotificationShadeWindowController.class); mStatusBarStateController = mock(StatusBarStateController.class); - mGroupManager = new NotificationGroupManager( + mGroupMembershipManager = new NotificationGroupManagerLegacy( mStatusBarStateController, () -> mock(PeopleNotificationIdentifier.class)); + mGroupExpansionManager = mGroupMembershipManager; mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController, - mock(KeyguardBypassController.class), mock(NotificationGroupManager.class), + mock(KeyguardBypassController.class), mock(NotificationGroupManagerLegacy.class), mock(ConfigurationControllerImpl.class)); - mGroupManager.setHeadsUpManager(mHeadsUpManager); + mGroupMembershipManager.setHeadsUpManager(mHeadsUpManager); mIconManager = new IconManager( mock(CommonNotifCollection.class), mock(LauncherApps.class), @@ -416,7 +418,8 @@ public class NotificationTestHelper { entry.getKey(), mock(ExpansionLogger.class), mock(KeyguardBypassController.class), - mGroupManager, + mGroupMembershipManager, + mGroupExpansionManager, mHeadsUpManager, mBindStage, mock(OnExpandClickListener.class), @@ -434,7 +437,7 @@ public class NotificationTestHelper { // This would be done as part of onAsyncInflationFinished, but we skip large amounts of // the callback chain, so we need to make up for not adding it to the group manager // here. - mGroupManager.onEntryAdded(entry); + mGroupMembershipManager.onEntryAdded(entry); return row; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 62b741c1938a..5264458cb9cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -47,9 +47,6 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.classifier.FalsingManagerFake; -import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationMediaManager; @@ -72,6 +69,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; @@ -80,8 +78,6 @@ import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.phone.NotificationGroupManager; -import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.leak.LeakDetector; @@ -115,7 +111,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private SysuiStatusBarStateController mBarState; @Mock private HeadsUpManagerPhone mHeadsUpManager; @Mock private NotificationBlockingHelperManager mBlockingHelperManager; - @Mock private NotificationGroupManager mGroupManager; + @Mock private NotificationGroupManagerLegacy mGroupMembershipManger; + @Mock private NotificationGroupManagerLegacy mGroupExpansionManager; @Mock private ExpandHelper mExpandHelper; @Mock private EmptyShadeView mEmptyShadeView; @Mock private NotificationRemoteInputManager mRemoteInputManager; @@ -128,6 +125,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private FeatureFlags mFeatureFlags; @Mock private SysuiStatusBarStateController mStatusBarStateController; @Mock private NotificationSwipeHelper mNotificationSwipeHelper; + @Mock NotificationStackScrollLayoutController mStackScrollLayoutController; private NotificationEntryManager mEntryManager; private int mOriginalInterruptionModelSetting; private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake(); @@ -159,10 +157,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mEntryManager = new NotificationEntryManager( mock(NotificationEntryManagerLogger.class), - mock(NotificationGroupManager.class), + mock(NotificationGroupManagerLegacy.class), new NotificationRankingManager( () -> mock(NotificationMediaManager.class), - mGroupManager, + mGroupMembershipManger, mHeadsUpManager, mock(NotificationFilter.class), mock(NotificationEntryManagerLogger.class), @@ -207,15 +205,19 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mock(NotifPipeline.class), mEntryManager, mock(NotifCollection.class), - mUiEventLoggerFake + mUiEventLoggerFake, + mGroupMembershipManger, + mGroupExpansionManager ); mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider, mNotificationSwipeHelper); mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelfController(notificationShelfController); mStackScroller.setStatusBar(mBar); - mStackScroller.setGroupManager(mGroupManager); mStackScroller.setEmptyShadeView(mEmptyShadeView); + when(mStackScrollLayoutController.getNoticationRoundessManager()) + .thenReturn(mock(NotificationRoundnessManager.class)); + mStackScroller.setController(mStackScrollLayoutController); // Stub out functionality that isn't necessary to test. doNothing().when(mBar) @@ -224,7 +226,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { anyBoolean(), anyBoolean(), anyBoolean()); - doNothing().when(mGroupManager).collapseAllGroups(); + doNothing().when(mGroupExpansionManager).collapseGroups(); doNothing().when(mExpandHelper).cancelImmediately(); doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java index f5d9fa07fa1c..32c682878c28 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.notification.stack; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.statusbar.StatusBarState.SHADE; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.argThat; @@ -45,11 +48,11 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -62,6 +65,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatcher; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -114,6 +118,11 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { private StatusBar mStatusBar; @Mock private ScrimController mScrimController; + @Mock + private NotificationGroupManagerLegacy mLegacyGroupManager; + + @Captor + ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; private NotificationStackScrollLayoutController mController; @@ -143,7 +152,9 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mResources, mNotificationSwipeHelperBuilder, mStatusBar, - mScrimController + mScrimController, + mLegacyGroupManager, + mLegacyGroupManager ); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); @@ -181,32 +192,49 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { } @Test - public void testUpdateEmptyShadeView_notificationsVisible() { + public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() { when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); mController.attach(mNotificationStackScrollLayout); + verify(mSysuiStatusBarStateController).addCallback( + mStateListenerArgumentCaptor.capture(), anyInt()); + StatusBarStateController.StateListener stateListener = + mStateListenerArgumentCaptor.getValue(); - mController.updateEmptyShadeView(true /* visible */); + setupShowEmptyShadeViewState(stateListener, true); + reset(mNotificationStackScrollLayout); + mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( true /* visible */, + true /* notifVisibleInShade */); + + setupShowEmptyShadeViewState(stateListener, false); reset(mNotificationStackScrollLayout); - mController.updateEmptyShadeView(false /* visible */); + mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( false /* visible */, true /* notifVisibleInShade */); } @Test - public void testUpdateEmptyShadeView_notificationsHidden() { + public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() { when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); mController.attach(mNotificationStackScrollLayout); + verify(mSysuiStatusBarStateController).addCallback( + mStateListenerArgumentCaptor.capture(), anyInt()); + StatusBarStateController.StateListener stateListener = + mStateListenerArgumentCaptor.getValue(); - mController.updateEmptyShadeView(true /* visible */); + setupShowEmptyShadeViewState(stateListener, true); + reset(mNotificationStackScrollLayout); + mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( true /* visible */, false /* notifVisibleInShade */); + + setupShowEmptyShadeViewState(stateListener, false); reset(mNotificationStackScrollLayout); - mController.updateEmptyShadeView(false /* visible */); + mController.updateShowEmptyShadeView(); verify(mNotificationStackScrollLayout).updateEmptyShadeView( false /* visible */, false /* notifVisibleInShade */); @@ -234,15 +262,12 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { public void testOnStatePostChange_verifyIfProfileIsPublic() { when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true); - ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor = - ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); - mController.attach(mNotificationStackScrollLayout); verify(mSysuiStatusBarStateController).addCallback( - stateListenerArgumentCaptor.capture(), anyInt()); + mStateListenerArgumentCaptor.capture(), anyInt()); StatusBarStateController.StateListener stateListener = - stateListenerArgumentCaptor.getValue(); + mStateListenerArgumentCaptor.getValue(); stateListener.onStatePostChange(); verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); @@ -299,6 +324,20 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { return argThat(new LogMatcher(category, type)); } + private void setupShowEmptyShadeViewState( + StatusBarStateController.StateListener statusBarStateListener, + boolean toShow) { + if (toShow) { + statusBarStateListener.onStateChanged(SHADE); + mController.setQsExpanded(false); + mController.getView().removeAllViews(); + } else { + statusBarStateListener.onStateChanged(KEYGUARD); + mController.setQsExpanded(true); + mController.getView().addContainerView(mock(ExpandableNotificationRow.class)); + } + } + static class LogMatcher implements ArgumentMatcher<LogMaker> { private int mCategory, mType; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index 2239b1b96ac8..57020eb08a7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -36,6 +36,7 @@ import com.android.systemui.statusbar.AlertingNotificationManagerTest; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -57,7 +58,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { private HeadsUpManagerPhone mHeadsUpManager; - @Mock private NotificationGroupManager mGroupManager; + @Mock private NotificationGroupManagerLegacy mGroupManager; @Mock private View mNotificationShadeWindowView; @Mock private VisualStabilityManager mVSManager; @Mock private StatusBar mBar; @@ -69,7 +70,7 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { private final class TestableHeadsUpManagerPhone extends HeadsUpManagerPhone { TestableHeadsUpManagerPhone( Context context, - NotificationGroupManager groupManager, + NotificationGroupManagerLegacy groupManager, VisualStabilityManager vsManager, StatusBarStateController statusBarStateController, KeyguardBypassController keyguardBypassController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java index 885dff39f7b3..2ece8be8d332 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java @@ -42,6 +42,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback; import com.android.systemui.statusbar.notification.row.RowContentBindParams; @@ -68,7 +69,7 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { @Rule public MockitoRule rule = MockitoJUnit.rule(); private NotificationGroupAlertTransferHelper mGroupAlertTransferHelper; - private NotificationGroupManager mGroupManager; + private NotificationGroupManagerLegacy mGroupManager; private HeadsUpManager mHeadsUpManager; @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private RowContentBindStage mBindStage; @@ -88,10 +89,10 @@ public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase { when(mNotificationEntryManager.getPendingNotificationsIterator()) .thenReturn(mPendingEntries.values()); - mGroupManager = new NotificationGroupManager( + mGroupManager = new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class)); - mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager); + mDependency.injectTestDependency(NotificationGroupManagerLegacy.class, mGroupManager); mGroupManager.setHeadsUpManager(mHeadsUpManager); when(mBindStage.getStageParams(any())).thenReturn(new RowContentBindParams()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java index 5a6f74a4c6aa..0aa009134440 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerLegacyTest.java @@ -33,6 +33,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.policy.HeadsUpManager; @@ -47,11 +48,11 @@ import org.mockito.junit.MockitoRule; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class NotificationGroupManagerTest extends SysuiTestCase { +public class NotificationGroupManagerLegacyTest extends SysuiTestCase { @Rule public MockitoRule rule = MockitoJUnit.rule(); - private NotificationGroupManager mGroupManager; + private NotificationGroupManagerLegacy mGroupManager; private final NotificationGroupTestHelper mGroupTestHelper = new NotificationGroupTestHelper(mContext); @@ -64,7 +65,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase { } private void initializeGroupManager() { - mGroupManager = new NotificationGroupManager( + mGroupManager = new NotificationGroupManagerLegacy( mock(StatusBarStateController.class), () -> mock(PeopleNotificationIdentifier.class)); mGroupManager.setHeadsUpManager(mHeadsUpManager); @@ -78,7 +79,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase { mGroupManager.onEntryAdded(summaryEntry); mGroupManager.onEntryAdded(childEntry); - assertTrue(mGroupManager.isOnlyChildInGroup(childEntry.getSbn())); + assertTrue(mGroupManager.isOnlyChildInGroup(childEntry)); } @Test @@ -90,7 +91,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase { mGroupManager.onEntryAdded(childEntry); mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification()); - assertTrue(mGroupManager.isChildInGroupWithSummary(childEntry.getSbn())); + assertTrue(mGroupManager.isChildInGroup(childEntry)); } @Test @@ -102,8 +103,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { mGroupManager.onEntryAdded(childEntry); mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification()); - assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.getSbn())); - assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry.getSbn())); + assertTrue(mGroupManager.isGroupSummary(summaryEntry)); + assertEquals(summaryEntry, mGroupManager.getGroupSummary(childEntry)); } @Test @@ -116,7 +117,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase { mGroupManager.onEntryRemoved(childEntry); - assertFalse(mGroupManager.isChildInGroupWithSummary(childEntry.getSbn())); + assertFalse(mGroupManager.isChildInGroup(childEntry)); } @Test @@ -129,8 +130,8 @@ public class NotificationGroupManagerTest extends SysuiTestCase { mGroupManager.onEntryRemoved(summaryEntry); - assertNull(mGroupManager.getGroupSummary(childEntry.getSbn())); - assertFalse(mGroupManager.isSummaryOfGroup(summaryEntry.getSbn())); + assertNull(mGroupManager.getGroupSummary(childEntry)); + assertFalse(mGroupManager.isGroupSummary(summaryEntry)); } @Test @@ -146,7 +147,7 @@ public class NotificationGroupManagerTest extends SysuiTestCase { // Child entries that are heads upped should be considered separate groups visually even if // they are the same group logically - assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry.getSbn())); - assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry.getSbn())); + assertEquals(childEntry, mGroupManager.getGroupSummary(childEntry)); + assertEquals(summaryEntry, mGroupManager.getLogicalGroupSummary(childEntry)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 453baa5e16fd..cf64ff2f8cd6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -74,12 +74,12 @@ import com.android.systemui.statusbar.notification.ConversationNotificationManag import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.InjectionInflationController; import org.junit.Before; @@ -118,7 +118,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private NotificationShelfController mNotificationShelfController; @Mock - private NotificationGroupManager mGroupManager; + private NotificationGroupManagerLegacy mGroupManager; @Mock private KeyguardStatusBarView mKeyguardStatusBar; @Mock @@ -174,8 +174,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { private KeyguardClockSwitch mKeyguardClockSwitch; private PanelViewController.TouchHandler mTouchHandler; @Mock - private ZenModeController mZenModeController; - @Mock private ConfigurationController mConfigurationController; @Mock private MediaHierarchyManager mMediaHiearchyManager; @@ -259,16 +257,16 @@ public class NotificationPanelViewTest extends SysuiTestCase { mKeyguardStateController, mStatusBarStateController, mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, - mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController, + mMetricsLogger, mActivityManager, mConfigurationController, flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, mConversationNotificationManager, mMediaHiearchyManager, mBiometricUnlockController, mStatusBarKeyguardViewManager, mNotificationStackScrollLayoutController, - mNotificationAreaController, - mKeyguardStatusViewComponentFactory); + mKeyguardStatusViewComponentFactory, + mGroupManager, + mNotificationAreaController); mNotificationPanelViewController.initDependencies( mStatusBar, - mGroupManager, mNotificationShelfController); mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); mNotificationPanelViewController.setBar(mPanelBar); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 2f4511329041..108327341f94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; @@ -27,7 +28,6 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; @@ -39,14 +39,13 @@ import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.SysuiTestCase; -import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.FaceAuthScreenBrightnessController; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; @@ -71,8 +70,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private LockPatternUtils mLockPatternUtils; @Mock - private KeyguardBouncer mBouncer; - @Mock private KeyguardStateController mKeyguardStateController; @Mock private StatusBar mStatusBar; @@ -94,6 +91,13 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { private KeyguardBypassController mBypassController; @Mock private FaceAuthScreenBrightnessController mFaceAuthScreenBrightnessController; + @Mock + private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; + @Mock + private KeyguardBouncerComponent mKeyguardBouncerComponent; + @Mock + private KeyguardBouncer mBouncer; + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Before @@ -102,7 +106,14 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mLockIconContainer.getParent()).thenReturn(mock(ViewGroup.class)); when(mLockIconContainer.animate()).thenReturn(mock(ViewPropertyAnimator.class, RETURNS_DEEP_STUBS)); - mStatusBarKeyguardViewManager = new TestableStatusBarKeyguardViewManager( + + when(mKeyguardBouncerComponentFactory.build( + any(ViewGroup.class), + any(KeyguardBouncer.BouncerExpansionCallback.class))) + .thenReturn(mKeyguardBouncerComponent); + when(mKeyguardBouncerComponent.createKeyguardBouncer()).thenReturn(mBouncer); + + mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager( getContext(), mViewMediatorCallback, mLockPatternUtils, @@ -113,12 +124,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mock(DockManager.class), mock(NotificationShadeWindowController.class), mKeyguardStateController, - mFaceAuthScreenBrightnessController, - mock(NotificationMediaManager.class)); + Optional.of(mFaceAuthScreenBrightnessController), + mock(NotificationMediaManager.class), + mKeyguardBouncerComponentFactory); mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer, - mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry, - mLockIconContainer, mNotificationContainer, mBypassController, - new FalsingManagerFake()); + mNotificationPanelView, mBiometrucUnlockController, + mLockIconContainer, mNotificationContainer, mBypassController); mStatusBarKeyguardViewManager.show(null); } @@ -267,38 +278,4 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { verify(action).onDismiss(); verify(cancelAction, never()).run(); } - - private class TestableStatusBarKeyguardViewManager extends StatusBarKeyguardViewManager { - - public TestableStatusBarKeyguardViewManager(Context context, - ViewMediatorCallback callback, - LockPatternUtils lockPatternUtils, - SysuiStatusBarStateController sysuiStatusBarStateController, - ConfigurationController configurationController, - KeyguardUpdateMonitor keyguardUpdateMonitor, - NavigationModeController navigationModeController, - DockManager dockManager, - NotificationShadeWindowController notificationShadeWindowController, - KeyguardStateController keyguardStateController, - FaceAuthScreenBrightnessController faceAuthScreenBrightnessController, - NotificationMediaManager notificationMediaManager) { - super(context, callback, lockPatternUtils, sysuiStatusBarStateController, - configurationController, keyguardUpdateMonitor, navigationModeController, - dockManager, notificationShadeWindowController, keyguardStateController, - Optional.of(faceAuthScreenBrightnessController), notificationMediaManager); - } - - @Override - public void registerStatusBar(StatusBar statusBar, ViewGroup container, - NotificationPanelViewController notificationPanelViewController, - BiometricUnlockController fingerprintUnlockController, - DismissCallbackRegistry dismissCallbackRegistry, - ViewGroup lockIconContainer, View notificationContainer, - KeyguardBypassController bypassController, FalsingManager falsingManager) { - super.registerStatusBar(statusBar, container, notificationPanelViewController, - fingerprintUnlockController, dismissCallbackRegistry, lockIconContainer, - notificationContainer, bypassController, falsingManager); - mBouncer = StatusBarKeyguardViewManagerTest.this.mBouncer; - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 3f631b1f6282..792637d8479b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -67,9 +67,9 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; @@ -122,8 +122,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private FeatureFlags mFeatureFlags; @Mock private NotifPipeline mNotifPipeline; - @Mock - private NotifCollection mNotifCollection; @Mock private ActivityIntentHelper mActivityIntentHelper; @@ -187,7 +185,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mUiBgExecutor, mEntryManager, mNotifPipeline, - mNotifCollection, mock(HeadsUpManagerPhone.class), mActivityStarter, mClickNotifier, @@ -198,7 +195,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mBubbleController, () -> mAssistManager, mRemoteInputManager, - mock(NotificationGroupManager.class), + mock(NotificationGroupManagerLegacy.class), mock(NotificationLockscreenUserManager.class), mShadeController, mKeyguardStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java index bf2bd38638ff..6fbbee22a73c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java @@ -36,6 +36,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -72,7 +73,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { mNotificationLockscreenUserManager); mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext, - mock(NotificationGroupManager.class), mNotificationLockscreenUserManager, + mock(NotificationGroupManagerLegacy.class), mNotificationLockscreenUserManager, mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager, mActivityStarter, mShadeController, new CommandQueue(mContext), mock(ActionClickLogger.class))); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 87aee3fd4794..5143596f0214 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -220,7 +220,6 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NetworkController mNetworkController; @Mock private VibratorHelper mVibratorHelper; @Mock private BubbleController mBubbleController; - @Mock private NotificationGroupManager mGroupManager; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController; @@ -376,7 +375,6 @@ public class StatusBarTest extends SysuiTestCase { mStatusBarStateController, mVibratorHelper, mBubbleController, - mGroupManager, mVisualStabilityManager, mDeviceProvisionedController, mNavigationBarController, diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java index 07e82111d4e5..5b46cb4ab378 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java @@ -294,7 +294,7 @@ class MultiFingerSwipe extends GestureMatcher { + Float.toString(mGestureDetectionThresholdPixels)); } if (getState() == STATE_CLEAR) { - if (moveDelta < mTouchSlop) { + if (moveDelta < (mTargetFingerCount * mTouchSlop)) { // This still counts as a touch not a swipe. continue; } else if (mStrokeBuffers[pointerIndex].size() == 0) { diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index e9c6882afdd8..8305be393ab1 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -608,7 +608,7 @@ public class TouchExplorer extends BaseEventStreamTransformation mReceivedPointerTracker.getReceivedPointerDownY(id) - rawEvent.getY(index); final double moveDelta = Math.hypot(deltaX, deltaY); - if (moveDelta < mTouchSlop) { + if (moveDelta < (2 * mTouchSlop)) { return; } } diff --git a/services/companion/TEST_MAPPING b/services/companion/TEST_MAPPING new file mode 100644 index 000000000000..63f54fa35158 --- /dev/null +++ b/services/companion/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "CtsOsTestCases", + "options": [ + { + "include-filter": "android.os.cts.CompanionDeviceManagerTest" + } + ] + } + ] +} diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 35e88eb804cb..7d81d412e369 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -2114,7 +2114,7 @@ public class AccountManagerService * Owner or system user account was renamed, rename the account for * those users with which the account was shared. */ - List<UserInfo> users = getUserManager().getUsers(true); + List<UserInfo> users = getUserManager().getAliveUsers(); for (UserInfo user : users) { if (user.isRestricted() && (user.restrictedProfileParentId == parentUserId)) { @@ -2373,7 +2373,7 @@ public class AccountManagerService int parentUserId = accounts.userId; if (canHaveProfile(parentUserId)) { // Remove from any restricted profiles that are sharing this account. - List<UserInfo> users = getUserManager().getUsers(true); + List<UserInfo> users = getUserManager().getAliveUsers(); for (UserInfo user : users) { if (user.isRestricted() && parentUserId == (user.restrictedProfileParentId)) { removeSharedAccountAsUser(account, user.id, callingUid); @@ -4267,7 +4267,7 @@ public class AccountManagerService */ @NonNull public AccountAndUser[] getAllAccounts() { - final List<UserInfo> users = getUserManager().getUsers(true); + final List<UserInfo> users = getUserManager().getAliveUsers(); final int[] userIds = new int[users.size()]; for (int i = 0; i < userIds.length; i++) { userIds[i] = users.get(i).id; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2d803437beb9..1502b7e0355c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17144,8 +17144,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) { - return mCpHelper.checkContentProviderUriPermission(uri, - userId, callingUid, modeFlags); + return mCpHelper.checkContentProviderUriPermission(uri, userId, callingUid, modeFlags); } @Override diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index bfba4afcd4e4..05a731d2b50b 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -198,22 +198,10 @@ public class ContentProviderHelper { if (providerRunning) { cpi = cpr.info; - String msg; if (r != null && cpr.canRunHere(r)) { - if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) { - throw new SecurityException("Content provider lookup " - + cpr.name.flattenToShortString() - + " failed: association not allowed with package " + msg); - } - checkTime(startTime, - "getContentProviderImpl: before checkContentProviderPermission"); - if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) - != null) { - throw new SecurityException(msg); - } - checkTime(startTime, - "getContentProviderImpl: after checkContentProviderPermission"); + checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser, + cpr.name.flattenToShortString(), startTime); // This provider has been published or is in the process // of being published... but it is also allowed to run @@ -234,26 +222,14 @@ public class ContentProviderHelper { } catch (RemoteException e) { } - if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) { - throw new SecurityException( - "Content provider lookup " + cpr.name.flattenToShortString() - + " failed: association not allowed with package " + msg); - } - checkTime(startTime, - "getContentProviderImpl: before checkContentProviderPermission"); - if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) - != null) { - throw new SecurityException(msg); - } - checkTime(startTime, - "getContentProviderImpl: after checkContentProviderPermission"); + checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, checkCrossUser, + cpr.name.flattenToShortString(), startTime); final long origId = Binder.clearCallingIdentity(); checkTime(startTime, "getContentProviderImpl: incProviderCountLocked"); - // In this case the provider instance already exists, so we can - // return it right away. + // In this case the provider instance already exists so we can return it right away. conn = incProviderCountLocked(r, cpr, token, callingUid, callingPackage, callingTag, stable, true, startTime, mService.mProcessList); @@ -328,19 +304,8 @@ public class ContentProviderHelper { cpi.applicationInfo = mService.getAppInfoForUser(cpi.applicationInfo, userId); checkTime(startTime, "getContentProviderImpl: got app info for user"); - String msg; - if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) { - throw new SecurityException("Content provider lookup " + name - + " failed: association not allowed with package " + msg); - } - checkTime(startTime, - "getContentProviderImpl: before checkContentProviderPermission"); - if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton)) - != null) { - throw new SecurityException(msg); - } - checkTime(startTime, - "getContentProviderImpl: after checkContentProviderPermission"); + checkAssociationAndPermissionLocked(r, cpi, callingUid, userId, !singleton, + name, startTime); if (!mService.mProcessesReady && !cpi.processName.equals("system")) { // If this content provider does not run in the system @@ -352,10 +317,12 @@ public class ContentProviderHelper { // If system providers are not installed yet we aggressively crash to avoid // creating multiple instance of these providers and then bad things happen! - if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp() - && "system".equals(cpi.processName)) { - throw new IllegalStateException("Cannot access system provider: '" - + cpi.authority + "' before system providers are installed!"); + synchronized (this) { + if (!mSystemProvidersInstalled && cpi.applicationInfo.isSystemApp() + && "system".equals(cpi.processName)) { + throw new IllegalStateException("Cannot access system provider: '" + + cpi.authority + "' before system providers are installed!"); + } } // Make sure that the user who owns this provider is running. If not, @@ -605,6 +572,23 @@ public class ContentProviderHelper { return cpr.newHolder(conn, false); } + private void checkAssociationAndPermissionLocked(ProcessRecord callingApp, ProviderInfo cpi, + int callingUid, int userId, boolean checkUser, String cprName, long startTime) { + String msg; + if ((msg = checkContentProviderAssociation(callingApp, callingUid, cpi)) != null) { + throw new SecurityException("Content provider lookup " + cprName + + " failed: association not allowed with package " + msg); + } + checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission"); + if ((msg = checkContentProviderPermission( + cpi, Binder.getCallingPid(), Binder.getCallingUid(), userId, checkUser, + callingApp != null ? callingApp.toString() : null)) + != null) { + throw new SecurityException(msg); + } + checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission"); + } + void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) { if (providers == null) { return; @@ -623,7 +607,7 @@ public class ContentProviderHelper { } final long origId = Binder.clearCallingIdentity(); - + boolean providersPublished = false; for (int i = 0, size = providers.size(); i < size; i++) { ContentProviderHolder src = providers.get(i); if (src == null || src.info == null || src.provider == null) { @@ -636,6 +620,7 @@ public class ContentProviderHelper { if (DEBUG_MU) { Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid); } + providersPublished = true; ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name); mProviderMap.putProviderByClass(comp, dst); @@ -673,8 +658,19 @@ public class ContentProviderHelper { dst.onProviderPublishStatusLocked(true); } dst.mRestartCount = 0; + } + + // update the app's oom adj value and each provider's usage stats + if (providersPublished) { mService.updateOomAdjLocked(r, true, OomAdjuster.OOM_ADJ_REASON_GET_PROVIDER); - maybeUpdateProviderUsageStatsLocked(r, src.info.packageName, src.info.authority); + for (int i = 0, size = providers.size(); i < size; i++) { + ContentProviderHolder src = providers.get(i); + if (src == null || src.info == null || src.provider == null) { + continue; + } + maybeUpdateProviderUsageStatsLocked(r, + src.info.packageName, src.info.authority); + } } Binder.restoreCallingIdentity(origId); @@ -997,17 +993,19 @@ public class ContentProviderHelper { + "; expected to find a valid ContentProvider for this authority"; } + final int callingPid = Binder.getCallingPid(); ProcessRecord r; + final String appName; synchronized (mService.mPidsSelfLocked) { - r = mService.mPidsSelfLocked.get(Binder.getCallingPid()); - } - if (r == null) { - return "Failed to find PID " + Binder.getCallingPid(); + r = mService.mPidsSelfLocked.get(callingPid); + if (r == null) { + return "Failed to find PID " + callingPid; + } + appName = r.toString(); } - synchronized (mService) { - return checkContentProviderPermissionLocked(cpi, r, userId, true); - } + return checkContentProviderPermission(cpi, callingPid, Binder.getCallingUid(), + userId, true, appName); } int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) { @@ -1163,13 +1161,14 @@ public class ContentProviderHelper { } } } - if (providers != null) { - mService.mSystemThread.installSystemProviders(providers); - } - synchronized (mService) { + synchronized (this) { + if (providers != null) { + mService.mSystemThread.installSystemProviders(providers); + } mSystemProvidersInstalled = true; } + mService.mConstants.start(mService.mContext.getContentResolver()); mService.mCoreSettingsObserver = new CoreSettingsObserver(mService); mService.mActivityTaskManager.installSystemProviders(); @@ -1305,10 +1304,8 @@ public class ContentProviderHelper { * given {@link ProviderInfo}. Final permission checking is always done * in {@link ContentProvider}. */ - private String checkContentProviderPermissionLocked(ProviderInfo cpi, ProcessRecord r, - int userId, boolean checkUser) { - final int callingPid = (r != null) ? r.pid : Binder.getCallingPid(); - final int callingUid = (r != null) ? r.uid : Binder.getCallingUid(); + private String checkContentProviderPermission(ProviderInfo cpi, int callingPid, int callingUid, + int userId, boolean checkUser, String appName) { boolean checkedGrants = false; if (checkUser) { // Looking for cross-user grants before enforcing the typical cross-users permissions @@ -1376,8 +1373,8 @@ public class ContentProviderHelper { suffix = " requires " + cpi.readPermission + " or " + cpi.writePermission; } final String msg = "Permission Denial: opening provider " + cpi.name - + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid - + ", uid=" + callingUid + ")" + suffix; + + " from " + (appName != null ? appName : "(null)") + + " (pid=" + callingPid + ", uid=" + callingUid + ")" + suffix; Slog.w(TAG, msg); return msg; } @@ -1398,18 +1395,17 @@ public class ContentProviderHelper { } ProviderInfo getProviderInfoLocked(String authority, @UserIdInt int userId, int pmFlags) { - ProviderInfo pi = null; ContentProviderRecord cpr = mProviderMap.getProviderByName(authority, userId); if (cpr != null) { - pi = cpr.info; + return cpr.info; } else { try { - pi = AppGlobals.getPackageManager().resolveContentProvider( + return AppGlobals.getPackageManager().resolveContentProvider( authority, PackageManager.GET_URI_PERMISSION_PATTERNS | pmFlags, userId); } catch (RemoteException ex) { + return null; } } - return pi; } private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String providerPkgName, @@ -1419,7 +1415,6 @@ public class ContentProviderHelper { return; } - UserState userState = mService.mUserController.getStartedUserState(app.userId); if (userState == null) return; final long now = SystemClock.elapsedRealtime(); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d59780d7f609..3f29eb5636ea 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -6528,8 +6528,8 @@ public class AudioService extends IAudioService.Stub CHECK_MODE_FOR_UID_PERIOD_MS); break; } - // For now just log the fact that an app is hogging the audio mode. - // TODO(b/160260850): remove abusive app from audio mode stack. + setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(), + h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID"); mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java index 32bb2db77ddc..d9c62df3f3ea 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java @@ -29,7 +29,6 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; import android.hardware.face.Face; -import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorProperties; import android.hardware.face.IFaceServiceReceiver; import android.os.Build; @@ -383,7 +382,7 @@ class Face10 implements IHwBinder.DeathRecipient { // is safe because authenticatorIds only change when A) new template has been enrolled, // or B) all templates are removed. mHandler.post(() -> { - for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) { + for (UserInfo user : UserManager.get(mContext).getAliveUsers()) { final int targetUserId = user.id; if (!mAuthenticatorIds.containsKey(targetUserId)) { scheduleUpdateActiveUserWithoutHandler(targetUserId); @@ -480,7 +479,8 @@ class Face10 implements IHwBinder.DeathRecipient { * notifying the previous caller that the interrupting operation is complete (e.g. the * interrupting client's challenge has been revoked, so that the interrupted client can * start retry logic if necessary). See - * {@link FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)} + * {@link + *android.hardware.face.FaceManager.GenerateChallengeCallback#onChallengeInterruptFinished(int)} * The only case of conflicting challenges is currently resetLockout --> enroll. So, the second * option seems better as it prioritizes the new operation, which is user-facing. */ diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java index c5c28227fd24..3754bd748781 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java @@ -440,7 +440,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient { // is safe because authenticatorIds only change when A) new template has been enrolled, // or B) all templates are removed. mHandler.post(() -> { - for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) { + for (UserInfo user : UserManager.get(mContext).getAliveUsers()) { final int targetUserId = user.id; if (!mAuthenticatorIds.containsKey(targetUserId)) { scheduleUpdateActiveUserWithoutHandler(targetUserId); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index ec12a971e445..b33aa0a6fad3 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -367,7 +367,7 @@ public class SyncManager { } private void removeStaleAccounts() { - for (UserInfo user : mUserManager.getUsers(true)) { + for (UserInfo user : mUserManager.getAliveUsers()) { // Skip any partially created/removed users if (user.partial) continue; Account[] accountsForUser = AccountManagerService.getSingleton().getAccounts( @@ -777,7 +777,7 @@ public class SyncManager { if (!mSyncStorageEngine.shouldGrantSyncAdaptersAccountAccess()) { return; } - List<UserInfo> users = mUserManager.getUsers(true); + List<UserInfo> users = mUserManager.getAliveUsers(); final int userCount = users.size(); for (int i = 0; i < userCount; i++) { UserHandle userHandle = users.get(i).getUserHandle(); diff --git a/services/core/java/com/android/server/inputmethod/TEST_MAPPING b/services/core/java/com/android/server/inputmethod/TEST_MAPPING new file mode 100644 index 000000000000..0ccd75dcbdce --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/base/core/java/android/view/inputmethod" + } + ] +} diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index e9a05a8aa16c..715e41c62a05 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -746,7 +746,7 @@ class LockSettingsStorage { public void dump(IndentingPrintWriter pw) { final UserManager um = UserManager.get(mContext); - for (UserInfo user : um.getUsers(false)) { + for (UserInfo user : um.getUsers()) { File userPath = getSyntheticPasswordDirectoryForUser(user.id); pw.println(String.format("User %d [%s]:", user.id, userPath.getAbsolutePath())); pw.increaseIndent(); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index a604625460a7..74b7bd76b047 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -1554,7 +1554,7 @@ abstract public class ManagedServices { if (!isEnabledForCurrentProfiles()) { return false; } - return this.userid == userId; + return userId == USER_ALL || userId == this.userid; } public boolean enabledAndUserMatches(int nid) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 04658555f22b..12419a9fcafa 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -262,7 +262,6 @@ import com.android.server.EventLogTags; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.UiThread; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; @@ -966,8 +965,7 @@ public class NotificationManagerService extends SystemService { nv.recycle(); } reportUserInteraction(r); - mAssistants.notifyAssistantActionClicked( - r.getSbn(), actionIndex, action, generatedByAssistant); + mAssistants.notifyAssistantActionClicked(r.getSbn(), action, generatedByAssistant); } } @@ -8629,12 +8627,25 @@ public class NotificationManagerService extends SystemService { ServiceManager.getService(Context.COMPANION_DEVICE_SERVICE)); } - private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { + @VisibleForTesting + boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) { if (!listener.enabledAndUserMatches(sbn.getUserId())) { return false; } - // TODO: remove this for older listeners. - return true; + return isInteractionVisibleToListener(listener, sbn.getUserId()); + } + + /** + * Returns whether the given assistant should be informed about interactions on the given user. + * + * Normally an assistant would be able to see all interactions on the current user and any + * associated profiles because they are notification listeners, but since NASes have one + * instance per user, we want to filter out interactions that are not for the user that the + * given NAS is bound in. + */ + private boolean isInteractionVisibleToListener(ManagedServiceInfo info, int userId) { + boolean isAssistantService = mAssistants.isServiceTokenValidLocked(info.service); + return !isAssistantService || info.isSameUser(userId); } private boolean isPackageSuspendedForUser(String pkg, int uid) { @@ -8856,8 +8867,6 @@ public class NotificationManagerService extends SystemService { } protected void onNotificationsSeenLocked(ArrayList<NotificationRecord> records) { - // There should be only one, but it's a list, so while we enforce - // singularity elsewhere, we keep it general here, to avoid surprises. for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { ArrayList<String> keys = new ArrayList<>(records.size()); for (NotificationRecord r : records) { @@ -8875,6 +8884,8 @@ public class NotificationManagerService extends SystemService { } protected void onPanelRevealed(int items) { + // send to all currently bounds NASes since notifications from both users will appear in + // the panel for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> { final INotificationListener assistant = (INotificationListener) info.service; @@ -8888,6 +8899,8 @@ public class NotificationManagerService extends SystemService { } protected void onPanelHidden() { + // send to all currently bounds NASes since notifications from both users will appear in + // the panel for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) { mHandler.post(() -> { final INotificationListener assistant = (INotificationListener) info.service; @@ -8976,7 +8989,7 @@ public class NotificationManagerService extends SystemService { } notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onNotificationVisibilityChanged(key, isVisible); @@ -8994,7 +9007,7 @@ public class NotificationManagerService extends SystemService { final String key = sbn.getKey(); notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onNotificationExpansionChanged(key, isUserAction, isExpanded); @@ -9010,7 +9023,7 @@ public class NotificationManagerService extends SystemService { final String key = sbn.getKey(); notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onNotificationDirectReply(key); @@ -9026,7 +9039,7 @@ public class NotificationManagerService extends SystemService { final String key = sbn.getKey(); notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onSuggestedReplySent( @@ -9043,12 +9056,12 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") void notifyAssistantActionClicked( - final StatusBarNotification sbn, int actionIndex, Notification.Action action, + final StatusBarNotification sbn, Notification.Action action, boolean generatedByAssistant) { final String key = sbn.getKey(); notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onActionClicked( @@ -9072,7 +9085,7 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification sbn, final String snoozeCriterionId) { notifyAssistantLocked( sbn, - false /* sameUserOnly */, + true /* sameUserOnly */, (assistant, sbnHolder) -> { try { assistant.onNotificationSnoozedUntilContext( @@ -9129,7 +9142,7 @@ public class NotificationManagerService extends SystemService { } protected void resetDefaultAssistantsIfNecessary() { - final List<UserInfo> activeUsers = mUm.getUsers(true); + final List<UserInfo> activeUsers = mUm.getAliveUsers(); for (UserInfo userInfo : activeUsers) { int userId = userInfo.getUserHandle().getIdentifier(); if (!hasUserSet(userId)) { @@ -9293,10 +9306,12 @@ public class NotificationManagerService extends SystemService { } public void onStatusBarIconsBehaviorChanged(boolean hideSilentStatusIcons) { + // send to all currently bounds NASes since notifications from both users will appear in + // the status bar for (final ManagedServiceInfo info : getServices()) { mHandler.post(() -> { final INotificationListener listener = (INotificationListener) info.service; - try { + try { listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons); } catch (RemoteException ex) { Slog.e(TAG, "unable to notify listener " @@ -9470,7 +9485,8 @@ public class NotificationManagerService extends SystemService { && changedHiddenNotifications.size() > 0; for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.isEnabledForCurrentProfiles()) { + if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener( + serviceInfo, ActivityManager.getCurrentUser())) { continue; } @@ -9489,12 +9505,7 @@ public class NotificationManagerService extends SystemService { final NotificationRankingUpdate update = makeRankingUpdateLocked( serviceInfo); - mHandler.post(new Runnable() { - @Override - public void run() { - notifyRankingUpdate(serviceInfo, update); - } - }); + mHandler.post(() -> notifyRankingUpdate(serviceInfo, update)); } } } @@ -9502,15 +9513,11 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") public void notifyListenerHintsChangedLocked(final int hints) { for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.isEnabledForCurrentProfiles()) { + if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener( + serviceInfo, ActivityManager.getCurrentUser())) { continue; } - mHandler.post(new Runnable() { - @Override - public void run() { - notifyListenerHintsChanged(serviceInfo, hints); - } - }); + mHandler.post(() -> notifyListenerHintsChanged(serviceInfo, hints)); } } @@ -9562,15 +9569,12 @@ public class NotificationManagerService extends SystemService { public void notifyInterruptionFilterChanged(final int interruptionFilter) { for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.isEnabledForCurrentProfiles()) { + if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener( + serviceInfo, ActivityManager.getCurrentUser())) { continue; } - mHandler.post(new Runnable() { - @Override - public void run() { - notifyInterruptionFilterChanged(serviceInfo, interruptionFilter); - } - }); + mHandler.post( + () -> notifyInterruptionFilterChanged(serviceInfo, interruptionFilter)); } } @@ -9579,15 +9583,16 @@ public class NotificationManagerService extends SystemService { if (channel == null) { return; } - for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) { + for (final ManagedServiceInfo info : getServices()) { + if (!info.enabledAndUserMatches(UserHandle.getCallingUserId()) + || !isInteractionVisibleToListener(info, UserHandle.getCallingUserId())) { continue; } BackgroundThread.getHandler().post(() -> { - if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) { + if (info.isSystem || hasCompanionDevice(info)) { notifyNotificationChannelChanged( - serviceInfo, pkg, user, channel, modificationType); + info, pkg, user, channel, modificationType); } }); } @@ -9599,15 +9604,16 @@ public class NotificationManagerService extends SystemService { if (group == null) { return; } - for (final ManagedServiceInfo serviceInfo : getServices()) { - if (!serviceInfo.enabledAndUserMatches(UserHandle.getCallingUserId())) { + for (final ManagedServiceInfo info : getServices()) { + if (!info.enabledAndUserMatches(UserHandle.getCallingUserId()) + || !isInteractionVisibleToListener(info, UserHandle.getCallingUserId())) { continue; } BackgroundThread.getHandler().post(() -> { - if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) { + if (info.isSystem || hasCompanionDevice(info)) { notifyNotificationChannelGroupChanged( - serviceInfo, pkg, user, group, modificationType); + info, pkg, user, group, modificationType); } }); } @@ -9626,9 +9632,6 @@ public class NotificationManagerService extends SystemService { private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) { - if (!info.enabledAndUserMatches(sbn.getUserId())) { - return; - } final INotificationListener listener = (INotificationListener) info.service; StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn); try { diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index a4debc16493a..d7a1ba2a93d4 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -288,7 +288,7 @@ public final class OverlayManagerService extends SystemService { private void initIfNeeded() { final UserManager um = getContext().getSystemService(UserManager.class); - final List<UserInfo> users = um.getUsers(true /*excludeDying*/); + final List<UserInfo> users = um.getAliveUsers(); synchronized (mLock) { final int userCount = users.size(); for (int i = 0; i < userCount; i++) { diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java index d8745abb4a07..0338ed802436 100644 --- a/services/core/java/com/android/server/pm/ApkChecksums.java +++ b/services/core/java/com/android/server/pm/ApkChecksums.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256; import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512; import static android.content.pm.PackageManager.WHOLE_MD5; @@ -27,11 +28,20 @@ import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA25 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; import android.content.pm.FileChecksum; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.os.Handler; +import android.os.SystemClock; +import android.os.incremental.IncrementalManager; +import android.os.incremental.IncrementalStorage; import android.util.ArrayMap; +import android.util.Pair; import android.util.Slog; import android.util.apk.ApkSignatureSchemeV2Verifier; import android.util.apk.ApkSignatureSchemeV3Verifier; @@ -43,6 +53,7 @@ import android.util.apk.SignatureInfo; import android.util.apk.SignatureNotFoundException; import android.util.apk.VerityBuilder; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.security.VerityUtils; import java.io.BufferedInputStream; @@ -72,32 +83,163 @@ public class ApkChecksums { static final String ALGO_SHA512 = "SHA512"; /** - * Fetch or calculate checksums for the specific file. + * Check back in 1 second after we detected we needed to wait for the APK to be fully available. + */ + private static final long PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS = 1000; + + /** + * 24 hours timeout to wait till all files are loaded. + */ + private static final long PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS = 1000 * 3600 * 24; + + /** + * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors. * - * @param split split name, null for base - * @param file to fetch checksums for + * NOTE: All getters should return the same instance for every call. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + static class Injector { + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + interface Producer<T> { + /** Produce an instance of type {@link T} */ + T produce(); + } + + private final Producer<Context> mContext; + private final Producer<Handler> mHandlerProducer; + private final Producer<IncrementalManager> mIncrementalManagerProducer; + + Injector(Producer<Context> context, Producer<Handler> handlerProducer, + Producer<IncrementalManager> incrementalManagerProducer) { + mContext = context; + mHandlerProducer = handlerProducer; + mIncrementalManagerProducer = incrementalManagerProducer; + } + + public Context getContext() { + return mContext.produce(); + } + + public Handler getHandler() { + return mHandlerProducer.produce(); + } + + public IncrementalManager getIncrementalManager() { + return mIncrementalManagerProducer.produce(); + } + } + + /** + * Fetch or calculate checksums for the collection of files. + * + * @param filesToChecksum split name, null for base and File to fetch checksums for * @param optional mask to fetch readily available checksums * @param required mask to forcefully calculate if not available * @param trustedInstallers array of certificate to trust, two specific cases: * null - trust anybody, * [] - trust nobody. + * @param statusReceiver to receive the resulting checksums */ - public static List<FileChecksum> getFileChecksums(String split, File file, + public static void getChecksums(List<Pair<String, File>> filesToChecksum, @PackageManager.FileChecksumKind int optional, @PackageManager.FileChecksumKind int required, - @Nullable Certificate[] trustedInstallers) { + @Nullable Certificate[] trustedInstallers, + @NonNull IntentSender statusReceiver, + @NonNull Injector injector) { + List<Map<Integer, FileChecksum>> result = new ArrayList<>(filesToChecksum.size()); + for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { + final String split = filesToChecksum.get(i).first; + final File file = filesToChecksum.get(i).second; + Map<Integer, FileChecksum> checksums = new ArrayMap<>(); + result.add(checksums); + + try { + getAvailableFileChecksums(split, file, optional | required, trustedInstallers, + checksums); + } catch (Throwable e) { + Slog.e(TAG, "Preferred checksum calculation error", e); + } + } + + long startTime = SystemClock.uptimeMillis(); + processRequiredChecksums(filesToChecksum, result, required, statusReceiver, injector, + startTime); + } + + private static void processRequiredChecksums(List<Pair<String, File>> filesToChecksum, + List<Map<Integer, FileChecksum>> result, + @PackageManager.FileChecksumKind int required, + @NonNull IntentSender statusReceiver, + @NonNull Injector injector, + long startTime) { + final boolean timeout = + SystemClock.uptimeMillis() - startTime >= PROCESS_REQUIRED_CHECKSUMS_TIMEOUT_MILLIS; + List<FileChecksum> allChecksums = new ArrayList<>(); + for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { + final String split = filesToChecksum.get(i).first; + final File file = filesToChecksum.get(i).second; + Map<Integer, FileChecksum> checksums = result.get(i); + + try { + if (!timeout || required != 0) { + if (needToWait(file, required, checksums, injector)) { + // Not ready, come back later. + injector.getHandler().postDelayed(() -> { + processRequiredChecksums(filesToChecksum, result, required, + statusReceiver, injector, startTime); + }, PROCESS_REQUIRED_CHECKSUMS_DELAY_MILLIS); + return; + } + + getRequiredFileChecksums(split, file, required, checksums); + } + allChecksums.addAll(checksums.values()); + } catch (Throwable e) { + Slog.e(TAG, "Required checksum calculation error", e); + } + } + + final Intent intent = new Intent(); + intent.putExtra(EXTRA_CHECKSUMS, + allChecksums.toArray(new FileChecksum[allChecksums.size()])); + + try { + statusReceiver.sendIntent(injector.getContext(), 1, intent, null, null); + } catch (IntentSender.SendIntentException e) { + Slog.w(TAG, e); + } + } + + /** + * Fetch readily available checksums - enforced by kernel or provided by Installer. + * + * @param split split name, null for base + * @param file to fetch checksums for + * @param kinds mask to fetch checksums + * @param trustedInstallers array of certificate to trust, two specific cases: + * null - trust anybody, + * [] - trust nobody. + * @param checksums resulting checksums + */ + private static void getAvailableFileChecksums(String split, File file, + @PackageManager.FileChecksumKind int kinds, + @Nullable Certificate[] trustedInstallers, + Map<Integer, FileChecksum> checksums) { final String filePath = file.getAbsolutePath(); - Map<Integer, FileChecksum> checksums = new ArrayMap<>(); - final int kinds = (optional | required); - // System enforced: FSI or v2/v3/v4 signatures. - if ((kinds & WHOLE_MERKLE_ROOT_4K_SHA256) != 0) { + + // Always available: FSI or IncFs. + if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { // Hashes in fs-verity and IncFS are always verified. FileChecksum checksum = extractHashFromFS(split, filePath); if (checksum != null) { checksums.put(checksum.getKind(), checksum); } } - if ((kinds & (PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512)) != 0) { + + // System enforced: v2/v3. + if (isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) || isRequired( + PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature( split, filePath, kinds); if (v2v3checksums != null) { @@ -106,11 +248,58 @@ public class ApkChecksums { } // TODO(b/160605420): Installer provided. - // TODO(b/160605420): Wait for Incremental to be fully loaded. + } + + /** + * Whether the file is available for checksumming or we need to wait. + */ + private static boolean needToWait(File file, + @PackageManager.FileChecksumKind int kinds, + Map<Integer, FileChecksum> checksums, + @NonNull Injector injector) throws IOException { + if (!isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums) + && !isRequired(WHOLE_MD5, kinds, checksums) + && !isRequired(WHOLE_SHA1, kinds, checksums) + && !isRequired(WHOLE_SHA256, kinds, checksums) + && !isRequired(WHOLE_SHA512, kinds, checksums) + && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA256, kinds, checksums) + && !isRequired(PARTIAL_MERKLE_ROOT_1M_SHA512, kinds, checksums)) { + return false; + } + + final String filePath = file.getAbsolutePath(); + if (!IncrementalManager.isIncrementalPath(filePath)) { + return false; + } + + IncrementalManager manager = injector.getIncrementalManager(); + if (manager == null) { + throw new IllegalStateException("IncrementalManager is missing."); + } + IncrementalStorage storage = manager.openStorage(filePath); + if (storage == null) { + throw new IllegalStateException( + "IncrementalStorage is missing for a path on IncFs: " + filePath); + } + + return !storage.isFileFullyLoaded(filePath); + } + + /** + * Fetch or calculate checksums for the specific file. + * + * @param split split name, null for base + * @param file to fetch checksums for + * @param kinds mask to forcefully calculate if not available + * @param checksums resulting checksums + */ + private static void getRequiredFileChecksums(String split, File file, + @PackageManager.FileChecksumKind int kinds, + Map<Integer, FileChecksum> checksums) { + final String filePath = file.getAbsolutePath(); // Manually calculating required checksums if not readily available. - if ((required & WHOLE_MERKLE_ROOT_4K_SHA256) != 0 && !checksums.containsKey( - WHOLE_MERKLE_ROOT_4K_SHA256)) { + if (isRequired(WHOLE_MERKLE_ROOT_4K_SHA256, kinds, checksums)) { try { byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash( filePath, /*salt=*/null, @@ -127,14 +316,23 @@ public class ApkChecksums { } } - calculateChecksumIfRequested(checksums, split, file, required, WHOLE_MD5); - calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA1); - calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA256); - calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA512); + calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_MD5); + calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA1); + calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA256); + calculateChecksumIfRequested(checksums, split, file, kinds, WHOLE_SHA512); - calculatePartialChecksumsIfRequested(checksums, split, file, required); + calculatePartialChecksumsIfRequested(checksums, split, file, kinds); + } - return new ArrayList<>(checksums.values()); + private static boolean isRequired(@PackageManager.FileChecksumKind int kind, + @PackageManager.FileChecksumKind int kinds, Map<Integer, FileChecksum> checksums) { + if ((kinds & kind) == 0) { + return false; + } + if (checksums.containsKey(kind)) { + return false; + } + return true; } private static FileChecksum extractHashFromFS(String split, String filePath) { @@ -170,7 +368,9 @@ public class ApkChecksums { PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2, false).contentDigests; } catch (PackageParser.PackageParserException e) { - Slog.e(TAG, "Signature verification error", e); + if (!(e.getCause() instanceof SignatureNotFoundException)) { + Slog.e(TAG, "Signature verification error", e); + } } if (contentDigests == null) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 4b246c3b330c..162bfee8848d 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -743,9 +743,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mStagingManager.createSession(session); } - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionCreated(session.sessionId, session.userId); - } + mCallbacks.notifySessionCreated(session.sessionId, session.userId); + writeSessionsAsync(); return sessionId; } @@ -1355,25 +1354,18 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements class InternalCallback { public void onSessionBadgingChanged(PackageInstallerSession session) { - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId); - } - + mCallbacks.notifySessionBadgingChanged(session.sessionId, session.userId); writeSessionsAsync(); } public void onSessionActiveChanged(PackageInstallerSession session, boolean active) { - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, - active); - } + mCallbacks.notifySessionActiveChanged(session.sessionId, session.userId, + active); } public void onSessionProgressChanged(PackageInstallerSession session, float progress) { - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, - progress); - } + mCallbacks.notifySessionProgressChanged(session.sessionId, session.userId, + progress); } public void onStagedSessionChanged(PackageInstallerSession session) { @@ -1389,17 +1381,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } public void onSessionFinished(final PackageInstallerSession session, boolean success) { - if ((session.params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { - mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); - } + mCallbacks.notifySessionFinished(session.sessionId, session.userId, success); mInstallHandler.post(new Runnable() { @Override public void run() { - if (session.isStaged()) { - if (!success) { - mStagingManager.abortSession(session); - } + if (session.isStaged() && !success) { + mStagingManager.abortSession(session); } synchronized (mSessions) { if (!session.isStaged() || !success) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index ed62362b04fb..51164ba412b3 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1561,19 +1561,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { dispatchSessionFinished(error, detailMessage, null); } - private void onSessionVerificationFailure(int error, String detailMessage) { - Slog.e(TAG, "Failed to verify session " + sessionId + " [" + detailMessage + "]"); + private void onSessionVerificationFailure(int error, String msg) { + final String msgWithErrorCode = PackageManager.installStatusToString(error, msg); + Slog.e(TAG, "Failed to verify session " + sessionId + " [" + msgWithErrorCode + "]"); // Session is sealed and committed but could not be verified, we need to destroy it. destroyInternal(); if (isStaged()) { setStagedSessionFailed( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, detailMessage); + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, msgWithErrorCode); // TODO(b/136257624): Remove this once all verification logic has been transferred out // of StagingManager. mStagingManager.notifyVerificationComplete(sessionId); } else { // Dispatch message to remove session from PackageInstallerService. - dispatchSessionFinished(error, detailMessage, null); + dispatchSessionFinished(error, msg, null); } } @@ -3306,8 +3307,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Send broadcast to default launcher only if it's a new install // TODO(b/144270665): Secure the usage of this broadcast. final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); - if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts() - && (params.installFlags & PackageManager.INSTALL_DRY_RUN) == 0) { + if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { mPm.sendSessionCommitBroadcast(generateInfoScrubbed(true /*icon*/), userId); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 344f9cfdbc96..b6b7e9321b16 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -40,7 +40,6 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import static android.content.pm.PackageManager.EXTRA_CHECKSUMS; import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -169,7 +168,6 @@ import android.content.pm.ComponentInfo; import android.content.pm.DataLoaderType; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; -import android.content.pm.FileChecksum; import android.content.pm.IDexModuleRegisterCallback; import android.content.pm.IPackageChangeObserver; import android.content.pm.IPackageDataObserver; @@ -933,6 +931,7 @@ public class PackageManagerService extends IPackageManager.Stub private final Object mLock; private final Installer mInstaller; private final Object mInstallLock; + private final Handler mBackgroundHandler; private final Executor mBackgroundExecutor; // ----- producers ----- @@ -955,7 +954,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, - Executor backgroundExecutor, + Handler backgroundHandler, Producer<ComponentResolver> componentResolverProducer, Producer<PermissionManagerServiceInternal> permissionManagerProducer, Producer<UserManagerService> userManagerProducer, @@ -977,7 +976,8 @@ public class PackageManagerService extends IPackageManager.Stub mInstaller = installer; mAbiHelper = abiHelper; mInstallLock = installLock; - mBackgroundExecutor = backgroundExecutor; + mBackgroundHandler = backgroundHandler; + mBackgroundExecutor = new HandlerExecutor(backgroundHandler); mComponentResolverProducer = new Singleton<>(componentResolverProducer); mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); mUserManagerProducer = new Singleton<>(userManagerProducer); @@ -1092,6 +1092,10 @@ public class PackageManagerService extends IPackageManager.Stub return mPlatformCompatProducer.get(this, mPackageManager); } + public Handler getBackgroundHandler() { + return mBackgroundHandler; + } + public Executor getBackgroundExecutor() { return mBackgroundExecutor; } @@ -2489,29 +2493,14 @@ public class PackageManagerService extends IPackageManager.Stub final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates( trustedInstallers) : null; - final Context context = mContext; mInjector.getBackgroundExecutor().execute(() -> { - final Intent intent = new Intent(); - List<FileChecksum> result = new ArrayList<>(); - for (int i = 0, size = filesToChecksum.size(); i < size; ++i) { - final String split = filesToChecksum.get(i).first; - final File file = filesToChecksum.get(i).second; - try { - result.addAll(ApkChecksums.getFileChecksums(split, file, optional, required, - trustedCerts)); - } catch (Throwable e) { - Slog.e(TAG, "Checksum calculation error", e); - } - } - intent.putExtra(EXTRA_CHECKSUMS, - result.toArray(new FileChecksum[result.size()])); - - try { - statusReceiver.sendIntent(context, 1, intent, null, null); - } catch (SendIntentException e) { - Slog.w(TAG, e); - } + ApkChecksums.Injector injector = new ApkChecksums.Injector( + () -> mContext, + () -> mInjector.getBackgroundHandler(), + () -> mContext.getSystemService(IncrementalManager.class)); + ApkChecksums.getChecksums(filesToChecksum, optional, required, trustedCerts, + statusReceiver, injector); }); } @@ -2684,7 +2673,7 @@ public class PackageManagerService extends IPackageManager.Stub Injector injector = new Injector( context, lock, installer, installLock, new PackageAbiHelperImpl(), - new HandlerExecutor(backgroundHandler), + backgroundHandler, (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), (i, pm) -> @@ -14541,13 +14530,11 @@ public class PackageManagerService extends IPackageManager.Stub Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.pkg); } - // A restore should be performed at this point if (a) the install - // succeeded, (b) the operation is not an update, and (c) the new - // package has not opted out of backup participation. + // A restore should be requested at this point if (a) the install + // succeeded, (b) the operation is not an update. final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null; - boolean allowBackup = res.pkg != null && res.pkg.isAllowBackup(); - boolean doRestore = !update && allowBackup; + boolean doRestore = !update; // Set up the post-install work request bookkeeping. This will be used // and cleaned up by the post-install event handling regardless of whether diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index dbe96e63d978..485868237895 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -33,6 +33,9 @@ "name": "CtsContentTestCases", "options": [ { + "include-filter": "android.content.pm.cts.ChecksumsTest" + }, + { "include-filter": "android.content.pm.cts.PackageManagerShellCommandTest" }, { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d137fd05f793..e44c8ab3f7f3 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -760,6 +760,8 @@ public class UserManagerService extends IUserManager.Stub { return null; } + // TODO(b/157921703): replace by getAliveUsers() or remove (so callers + // explicitly call the 3-booleans version) public @NonNull List<UserInfo> getUsers(boolean excludeDying) { return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */ true); diff --git a/services/core/java/com/android/server/slice/SliceFullAccessList.java b/services/core/java/com/android/server/slice/SliceFullAccessList.java index 6f5afa207d31..d25ddf877951 100644 --- a/services/core/java/com/android/server/slice/SliceFullAccessList.java +++ b/services/core/java/com/android/server/slice/SliceFullAccessList.java @@ -101,7 +101,7 @@ public class SliceFullAccessList { public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException { // upgrade xml int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0); - final List<UserInfo> activeUsers = UserManager.get(mContext).getUsers(true); + final List<UserInfo> activeUsers = UserManager.get(mContext).getAliveUsers(); for (UserInfo userInfo : activeUsers) { upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier()); } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 0c85387be695..386f390c6cb9 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -379,7 +379,7 @@ public class TrustManagerService extends SystemService { } private void updateTrustAll() { - List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */); + List<UserInfo> userInfos = mUserManager.getAliveUsers(); for (UserInfo userInfo : userInfos) { updateTrust(userInfo.id, 0); } @@ -485,7 +485,7 @@ public class TrustManagerService extends SystemService { List<UserInfo> userInfos; if (userIdOrAll == UserHandle.USER_ALL) { - userInfos = mUserManager.getUsers(true /* excludeDying */); + userInfos = mUserManager.getAliveUsers(); } else { userInfos = new ArrayList<>(); userInfos.add(mUserManager.getUserInfo(userIdOrAll)); @@ -644,7 +644,7 @@ public class TrustManagerService extends SystemService { } List<UserInfo> userInfos; if (userId == UserHandle.USER_ALL) { - userInfos = mUserManager.getUsers(true /* excludeDying */); + userInfos = mUserManager.getAliveUsers(); } else { userInfos = new ArrayList<>(); userInfos.add(mUserManager.getUserInfo(userId)); @@ -1171,7 +1171,7 @@ public class TrustManagerService extends SystemService { fout.println("disabled because the third-party apps can't run yet."); return; } - final List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */); + final List<UserInfo> userInfos = mUserManager.getAliveUsers(); mHandler.runWithScissors(new Runnable() { @Override public void run() { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 56261c4fce97..7d4d5629dacd 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -401,7 +401,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final ActivityTaskManagerService mAtmService; final ActivityInfo info; // activity info provided by developer in AndroidManifest - // Non-null only for application tokens. // TODO: rename to mActivityToken final ActivityRecord.Token appToken; // Which user is this running for? @@ -5480,10 +5479,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void updateReportedVisibilityLocked() { - if (appToken == null) { - return; - } - if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this); final int count = mChildren.size(); @@ -6330,8 +6325,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) { - final IBinder binder = - (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null; + final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; setOrientation(requestedOrientation, binder, this); // Push the new configuration to the requested app in case where it's not pushed, e.g. when @@ -7713,9 +7707,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void writeNameToProto(ProtoOutputStream proto, long fieldId) { - if (appToken != null) { - proto.write(fieldId, appToken.getName()); - } + proto.write(fieldId, appToken.getName()); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index be7a6aed7489..f206259b8fe0 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1710,8 +1710,9 @@ class ActivityStarter { mRootWindowContainer.startPowerModeLaunchIfNeeded( false /* forceSend */, mStartActivity); - mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(), - newTask, mKeepCurTransition, mOptions); + mTargetStack.startActivityLocked(mStartActivity, + topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask, + mKeepCurTransition, mOptions); if (mDoResume) { final ActivityRecord topTaskActivity = mStartActivity.getTask().topRunningActivityLocked(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 6a8cbfbb5840..c58b5b50d508 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -125,6 +125,7 @@ import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK; 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.WindowContainer.POSITION_TOP; +import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import android.Manifest; import android.annotation.IntDef; @@ -5465,8 +5466,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { updateResumedAppTrace(r); mLastResumedActivity = r; - r.getDisplay().setFocusedApp(r, true); - + final boolean changed = r.getDisplay().setFocusedApp(r); + if (changed) { + mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, + true /*updateInputWindows*/); + } if (prevTask == null || task != prevTask) { if (prevTask != null) { mTaskChangeNotificationController.notifyTaskFocusChanged(prevTask.mTaskId, false); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2f7cc69b01a7..aa8069a76330 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -115,13 +115,11 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIG import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS; 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.WindowManagerService.H.REPORT_FOCUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS; import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT; import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION; -import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS; @@ -649,8 +647,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> { final ActivityRecord focusedApp = mFocusedApp; - ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b", - w, w.mAttrs.flags, w.canReceiveKeys()); + ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s", + w, w.mAttrs.flags, w.canReceiveKeys(), + w.canReceiveKeysReason(false /* fromUserTouch */)); if (!w.canReceiveKeys()) { return false; @@ -3072,7 +3071,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ WindowState findFocusedWindowIfNeeded(int topFocusedDisplayId) { return (mWmService.mPerDisplayFocusEnabled || topFocusedDisplayId == INVALID_DISPLAY) - ? findFocusedWindow() : null; + ? findFocusedWindow() : null; } WindowState findFocusedWindow() { @@ -3081,7 +3080,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */); if (mTmpWindow == null) { - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows."); + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows, display=%d", + getDisplayId()); return null; } return mTmpWindow; @@ -3116,18 +3116,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) { assignWindowLayers(false /* setLayoutNeeded */); } - } - if (imWindowChanged) { - mWmService.mWindowsChanged = true; - setLayoutNeeded(); - newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId); - } - if (mCurrentFocus != newFocus) { - mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget(); + if (imWindowChanged) { + mWmService.mWindowsChanged = true; + setLayoutNeeded(); + newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId); + } } - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s", + ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s", mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4)); final WindowState oldFocus = mCurrentFocus; mCurrentFocus = newFocus; @@ -3185,9 +3182,25 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mode == UPDATE_FOCUS_PLACING_SURFACES) { pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM; } + + // Notify the accessibility manager for the change so it has the windows before the newly + // focused one starts firing events. + // TODO(b/151179149) investigate what info accessibility service needs before input can + // dispatch focus to clients. + if (mWmService.mAccessibilityController != null) { + mWmService.mH.sendMessage(PooledLambda.obtainMessage( + this::updateAccessibilityOnWindowFocusChanged, + mWmService.mAccessibilityController)); + } + + mLastFocus = mCurrentFocus; return true; } + void updateAccessibilityOnWindowFocusChanged(AccessibilityController accessibilityController) { + accessibilityController.onWindowFocusChangedNotLocked(getDisplayId()); + } + private static void onWindowFocusChanged(WindowState oldFocus, WindowState newFocus) { final Task focusedTask = newFocus != null ? newFocus.getTask() : null; final Task unfocusedTask = oldFocus != null ? oldFocus.getTask() : null; @@ -3219,6 +3232,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (mFocusedApp == newFocus) { return false; } + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "setFocusedApp %s displayId=%d Callers=%s", + newFocus, getDisplayId(), Debug.getCallers(4)); mFocusedApp = newFocus; getInputMonitor().setFocusedAppLw(newFocus); updateTouchExcludeRegion(); @@ -4708,7 +4723,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Traverse all windows top down to assemble the gesture exclusion rects. // For each window, we only take the rects that fall within its touchable region. forAllWindows(w -> { - if (w.cantReceiveTouchInput() || !w.isVisible() + if (!w.canReceiveTouchInput() || !w.isVisible() || (w.getAttrs().flags & FLAG_NOT_TOUCHABLE) != 0 || unhandled.isEmpty()) { return; @@ -5225,30 +5240,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp && (mAtmService.mRunningVoice == null); } - void setFocusedApp(ActivityRecord r, boolean moveFocusNow) { - final ActivityRecord newFocus; - final IBinder token = r.appToken; - if (token == null) { - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Clearing focused app, displayId=%d", - mDisplayId); - newFocus = null; - } else { - newFocus = mWmService.mRoot.getActivityRecord(token); - if (newFocus == null) { - Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token - + ", displayId=" + mDisplayId); - } - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, - "Set focused app to: %s moveFocusNow=%b displayId=%d", newFocus, - moveFocusNow, mDisplayId); - } - - final boolean changed = setFocusedApp(newFocus); - if (moveFocusNow && changed) { - mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, - true /*updateInputWindows*/); - } - } void ensureActivitiesVisible(ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients) { diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 999aab982816..ec62ed44c640 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -114,7 +114,7 @@ class DragDropController { final WindowState callingWin = mService.windowForClientLocked( null, window, false); - if (callingWin == null || callingWin.cantReceiveTouchInput()) { + if (callingWin == null || !callingWin.canReceiveTouchInput()) { Slog.w(TAG_WM, "Bad requesting window " + window); return null; // !!! TODO: throw here? } diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index e166bfc08ad4..0978636ea502 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -24,6 +24,7 @@ import android.view.InputApplicationHandle; import android.view.KeyEvent; import android.view.WindowManager; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.am.ActivityManagerService; import com.android.server.input.InputManagerService; import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow; @@ -252,7 +253,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal // All the calls below need to happen without the WM lock held since they call into AM. mService.mAtmInternal.saveANRState(reason); - if (activity != null && activity.appToken != null) { + if (activity != null) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. final boolean abort = activity.keyDispatchingTimedOut(reason, windowPid); @@ -410,6 +411,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal requestRefreshConfiguration = dispatchPointerCaptureChanged(focusedWindow, false); } mFocusedWindow.set(newFocusedWindow); + mService.mH.sendMessage(PooledLambda.obtainMessage(mService::reportFocusChanged, + oldToken, newToken)); return requestRefreshConfiguration; } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index fb511e032c98..4efd687b7bb4 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -370,7 +370,8 @@ final class InputMonitor { * Layer assignment is assumed to be complete by the time this is called. */ public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) { - ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s", newWindow); + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d", + newWindow, mDisplayId); if (newWindow != mInputFocus) { if (newWindow != null && newWindow.canReceiveKeys()) { @@ -493,7 +494,7 @@ final class InputMonitor { final int type = w.mAttrs.type; final boolean isVisible = w.isVisibleLw(); if (inputChannel == null || inputWindowHandle == null || w.mRemoved - || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { + || (!w.canReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) { if (w.mWinAnimator.hasSurface()) { // Assign an InputInfo with type to the overlay window which can't receive input // event. This is used to omit Surfaces from occlusion detection. diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 21e30ce0a495..6182a55b6e52 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -40,7 +40,6 @@ import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY; import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; - import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; @@ -90,7 +89,6 @@ import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREEN import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; import static com.android.server.wm.WindowSurfacePlacer.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING; - import static java.lang.Integer.MAX_VALUE; import android.annotation.IntDef; @@ -129,6 +127,7 @@ import android.os.UserHandle; import android.os.storage.StorageManager; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.IntArray; @@ -162,7 +161,6 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Set; @@ -214,7 +212,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> private int mTopFocusedDisplayId = INVALID_DISPLAY; // Map from the PID to the top most app which has a focused window of the process. - final HashMap<Integer, ActivityRecord> mTopFocusedAppByProcess = new HashMap<>(); + final ArrayMap<Integer, ActivityRecord> mTopFocusedAppByProcess = new ArrayMap<>(); // Only a separate transaction until we separate the apply surface changes // transaction from the global transaction. @@ -480,8 +478,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mTopFocusedDisplayId = topFocusedDisplayId; mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId); mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId); - ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", - topFocusedDisplayId); + ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId); } return changed; } @@ -2289,10 +2286,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { final DisplayContent display = getChildAt(displayNdx); - if (display.shouldSleep()) { - continue; - } - final boolean curResult = result; boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas( (taskDisplayArea, resumed) -> { diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java index 33935d61ead2..7df2b407557d 100644 --- a/services/core/java/com/android/server/wm/SurfaceAnimator.java +++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java @@ -26,6 +26,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.DebugUtils; import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; @@ -429,7 +430,8 @@ class SurfaceAnimator { void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mLeash="); pw.print(mLeash); - pw.print(" mAnimationType=" + mAnimationType); + pw.print(" mAnimationType=" + DebugUtils.valueToString(SurfaceAnimator.class, + "ANIMATION_TYPE_", mAnimationType)); pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : ""); pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation); if (mAnimation != null) { @@ -442,56 +444,56 @@ class SurfaceAnimator { * No animation is specified. * @hide */ - static final int ANIMATION_TYPE_NONE = 0; + public static final int ANIMATION_TYPE_NONE = 0; /** * Animation for an app transition. * @hide */ - static final int ANIMATION_TYPE_APP_TRANSITION = 1; + public static final int ANIMATION_TYPE_APP_TRANSITION = 1; /** * Animation for screen rotation. * @hide */ - static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1; + public static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1; /** * Animation for dimming. * @hide */ - static final int ANIMATION_TYPE_DIMMER = 1 << 2; + public static final int ANIMATION_TYPE_DIMMER = 1 << 2; /** * Animation for recent apps. * @hide */ - static final int ANIMATION_TYPE_RECENTS = 1 << 3; + public static final int ANIMATION_TYPE_RECENTS = 1 << 3; /** * Animation for a {@link WindowState} without animating the activity. * @hide */ - static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4; + public static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4; /** * Animation to control insets. This is actually not an animation, but is used to give the * client a leash over the system window causing insets. * @hide */ - static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5; + public static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5; /** * Animation when a fixed rotation transform is applied to a window token. * @hide */ - static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6; + public static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6; /** * Bitmask to include all animation types. This is NOT an {@link AnimationType} * @hide */ - static final int ANIMATION_TYPE_ALL = -1; + public static final int ANIMATION_TYPE_ALL = -1; /** * The type of the animation. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 19bf451cec05..bfaaf462ed51 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -6348,7 +6348,7 @@ class Task extends WindowContainer<WindowContainer> { return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea()); } - void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity, + void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity, boolean newTask, boolean keepCurTransition, ActivityOptions options) { Task rTask = r.getTask(); final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront(); @@ -7585,7 +7585,11 @@ class Task extends WindowContainer<WindowContainer> { // Do not sleep activities in this stack if we're marked as focused and the keyguard // is in the process of going away. if (isFocusedStackOnDisplay() - && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) { + && mStackSupervisor.getKeyguardController().isKeyguardGoingAway() + // Avoid resuming activities on secondary displays since we don't want bubble + // activities to be resumed while bubble is still collapsed. + // TODO(b/113840485): Having keyguard going away state for secondary displays. + && display.isDefaultDisplay) { return false; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 1b779c6a0cc9..63a595e3bc17 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -38,6 +38,7 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import android.util.SparseBooleanArray; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; @@ -50,6 +51,7 @@ import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -206,7 +208,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final DeathRecipient mDeathRecipient; private final ArrayList<Task> mOrganizedTasks = new ArrayList<>(); private final int mUid; - private boolean mInterceptBackPressedOnTaskRoot; TaskOrganizerState(ITaskOrganizer organizer, int uid) { final Consumer<Runnable> deferTaskOrgCallbacksConsumer = @@ -224,10 +225,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mUid = uid; } - void setInterceptBackPressedOnTaskRoot(boolean interceptBackPressed) { - mInterceptBackPressedOnTaskRoot = interceptBackPressed; - } - void addTask(Task t) { if (t.mTaskAppearedSent) return; @@ -247,6 +244,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mOrganizer.onTaskVanished(t); } mOrganizedTasks.remove(t); + mInterceptBackPressedOnRootTasks.remove(t.mTaskId); } void dispose() { @@ -278,6 +276,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); + // Set of organized tasks (by taskId) that dispatch back pressed to their organizers + private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet(); private final ActivityTaskManagerService mService; @@ -623,7 +623,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public void setInterceptBackPressedOnTaskRoot(ITaskOrganizer organizer, + public void setInterceptBackPressedOnTaskRoot(WindowContainerToken token, boolean interceptBackPressed) { enforceStackPermission("setInterceptBackPressedOnTaskRoot()"); final long origId = Binder.clearCallingIdentity(); @@ -631,9 +631,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { synchronized (mGlobalLock) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set intercept back pressed on root=%b", interceptBackPressed); - final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); - if (state != null) { - state.setInterceptBackPressedOnTaskRoot(interceptBackPressed); + final Task task = WindowContainer.fromBinder(token.asBinder()).asTask(); + if (task == null) { + Slog.w(TAG, "Could not resolve task from token"); + return; + } + if (interceptBackPressed) { + mInterceptBackPressedOnRootTasks.add(task.mTaskId); + } else { + mInterceptBackPressedOnRootTasks.remove(task.mTaskId); } } } finally { @@ -642,15 +648,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } public boolean handleInterceptBackPressedOnTaskRoot(Task task) { - if (task == null || !task.isOrganized()) { + if (task == null || !task.isOrganized() + || !mInterceptBackPressedOnRootTasks.contains(task.mTaskId)) { return false; } final TaskOrganizerState state = mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder()); - if (!state.mInterceptBackPressedOnTaskRoot) { - return false; - } - state.mOrganizer.onBackPressedOnTaskRoot(task); return true; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c45ccb6e17e3..19179a808d7c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -376,8 +376,11 @@ public class WindowManagerService extends IWindowManager.Stub private static final String BOOT_ANIMATION_SERVICE = "bootanim"; static final int UPDATE_FOCUS_NORMAL = 0; + /** Caller will assign layers */ static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1; + /** Caller is performing surface placement */ static final int UPDATE_FOCUS_PLACING_SURFACES = 2; + /** Caller will performSurfacePlacement */ static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3; /** Indicates we are removing the focused window when updating the focus. */ static final int UPDATE_FOCUS_REMOVING_FOCUS = 4; @@ -4730,12 +4733,30 @@ public class WindowManagerService extends IWindowManager.Stub return false; } + void reportFocusChanged(IBinder oldToken, IBinder newToken) { + WindowState lastFocus; + WindowState newFocus; + synchronized (mGlobalLock) { + lastFocus = mInputToWindowMap.get(oldToken); + newFocus = mInputToWindowMap.get(newToken); + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastFocus, newFocus); + } + + if (newFocus != null) { + newFocus.reportFocusChangedSerialized(true); + notifyFocusChanged(); + } + + if (lastFocus != null) { + lastFocus.reportFocusChangedSerialized(false); + } + } + // ------------------------------------------------------------- // Async Handler // ------------------------------------------------------------- final class H extends android.os.Handler { - public static final int REPORT_FOCUS_CHANGE = 2; public static final int WINDOW_FREEZE_TIMEOUT = 11; public static final int PERSIST_ANIMATION_SCALE = 14; @@ -4788,50 +4809,6 @@ public class WindowManagerService extends IWindowManager.Stub Slog.v(TAG_WM, "handleMessage: entry what=" + msg.what); } switch (msg.what) { - case REPORT_FOCUS_CHANGE: { - final DisplayContent displayContent = (DisplayContent) msg.obj; - WindowState lastFocus; - WindowState newFocus; - - AccessibilityController accessibilityController = null; - - synchronized (mGlobalLock) { - if (mAccessibilityController != null) { - accessibilityController = mAccessibilityController; - } - - lastFocus = displayContent.mLastFocus; - newFocus = displayContent.mCurrentFocus; - if (lastFocus == newFocus) { - // Focus is not changing, so nothing to do. - return; - } - displayContent.mLastFocus = newFocus; - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus moving from %s" - + " to %s displayId=%d", lastFocus, newFocus, - displayContent.getDisplayId()); - } - - // First notify the accessibility manager for the change so it has - // the windows before the newly focused one starts firing events. - if (accessibilityController != null) { - accessibilityController.onWindowFocusChangedNotLocked( - displayContent.getDisplayId()); - } - - if (newFocus != null) { - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Gaining focus: %s", newFocus); - newFocus.reportFocusChangedSerialized(true); - notifyFocusChanged(); - } - - if (lastFocus != null) { - ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Losing focus: %s", lastFocus); - lastFocus.reportFocusChangedSerialized(false); - } - break; - } - case WINDOW_FREEZE_TIMEOUT: { final DisplayContent displayContent = (DisplayContent) msg.obj; synchronized (mGlobalLock) { @@ -7994,6 +7971,8 @@ public class WindowManagerService extends IWindowManager.Stub return; } + ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "onPointerDownOutsideFocusLocked called on %s", + touchedWindow); final DisplayContent displayContent = touchedWindow.getDisplayContent(); if (!displayContent.isOnTop()) { displayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP, displayContent, @@ -8022,10 +8001,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - try { - mActivityTaskManager.setFocusedTask(task.mTaskId); - } catch (RemoteException e) { - } + mAtmService.setFocusedTask(task.mTaskId); } /** diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9ff33b18cb89..84a9c750d2d3 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2884,12 +2884,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return canReceiveKeys(false /* fromUserTouch */); } + public String canReceiveKeysReason(boolean fromUserTouch) { + return "fromTouch= " + fromUserTouch + + " isVisibleOrAdding=" + isVisibleOrAdding() + + " mViewVisibility=" + mViewVisibility + + " mRemoveOnExit=" + mRemoveOnExit + + " flags=" + mAttrs.flags + + " appWindowsAreFocusable=" + + (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch)) + + " canReceiveTouchInput=" + canReceiveTouchInput() + + " displayIsOnTop=" + getDisplayContent().isOnTop() + + " displayIsTrusted=" + getDisplayContent().isTrusted(); + } + public boolean canReceiveKeys(boolean fromUserTouch) { final boolean canReceiveKeys = isVisibleOrAdding() && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) && (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch)) - && !cantReceiveTouchInput(); + && canReceiveTouchInput(); if (!canReceiveKeys) { return false; } @@ -2907,15 +2920,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return showBecauseOfActivity || showBecauseOfWindow; } - /** @return {@code false} if this window desires touch events. */ - boolean cantReceiveTouchInput() { - if (mActivityRecord == null || mActivityRecord.getTask() == null) { - return false; + /** + * @return {@code true} if this window can receive touches based on among other things, + * windowing state and recents animation state. + **/ + boolean canReceiveTouchInput() { + if (mActivityRecord == null || mActivityRecord.getTask() == null) { + return true; } - return mActivityRecord.getTask().getRootTask().shouldIgnoreInput() - || !mActivityRecord.mVisibleRequested - || isRecentsAnimationConsumingAppInput(); + return !mActivityRecord.getTask().getRootTask().shouldIgnoreInput() + && mActivityRecord.mVisibleRequested + && !isRecentsAnimationConsumingAppInput(); } /** diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 22e309cdc2b4..80455833a3eb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5782,9 +5782,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - final CallerIdentity identity = getCallerIdentity(); - Preconditions.checkCallAuthorization(isSystemUid(identity) || isRootUid(identity) - || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)); final ActiveAdmin admin; synchronized (getLockObject()) { @@ -9438,8 +9435,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(isDeviceOwner(identity)); return mInjector.binderWithCleanCallingIdentity(() -> { - final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true - /*excludeDying*/); + final List<UserInfo> userInfos = mInjector.getUserManager().getAliveUsers(); final List<UserHandle> userHandles = new ArrayList<>(); for (UserInfo userInfo : userInfos) { UserHandle userHandle = userInfo.getUserHandle(); @@ -10362,7 +10358,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void maybeClearLockTaskPolicyLocked() { mInjector.binderWithCleanCallingIdentity(() -> { - final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true); + final List<UserInfo> userInfos = mUserManager.getAliveUsers(); for (int i = userInfos.size() - 1; i >= 0; i--) { int userId = userInfos.get(i).id; if (canUserUseLockTaskLocked(userId)) { @@ -10849,7 +10845,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * them. */ void updateUserSetupCompleteAndPaired() { - List<UserInfo> users = mUserManager.getUsers(true); + List<UserInfo> users = mUserManager.getAliveUsers(); final int N = users.size(); for (int i = 0; i < N; i++) { int userHandle = users.get(i).id; @@ -12052,14 +12048,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public boolean isSystemOnlyUser(ComponentName admin) { - Objects.requireNonNull(admin, "ComponentName is null"); - final CallerIdentity identity = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(identity)); - return UserManager.isSplitSystemUser() && identity.getUserId() == UserHandle.USER_SYSTEM; - } - - @Override public void reboot(ComponentName admin) { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity identity = getCallerIdentity(admin); @@ -12579,7 +12567,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private boolean areAllUsersAffiliatedWithDeviceLocked() { return mInjector.binderWithCleanCallingIdentity(() -> { - final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true); + final List<UserInfo> userInfos = mUserManager.getAliveUsers(); for (int i = 0; i < userInfos.size(); i++) { int userId = userInfos.get(i).id; if (!isUserAffiliatedWithDeviceLocked(userId)) { @@ -13048,7 +13036,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } else { // Caller is the device owner: Look for profile owners that it can bind to. - final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true); + final List<UserInfo> userInfos = mUserManager.getAliveUsers(); for (int i = 0; i < userInfos.size(); i++) { final int userId = userInfos.get(i).id; if (userId != callingUserId && canUserBindToDeviceOwnerLocked(userId)) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index 3cdd482ffa37..7649af4ee911 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -169,7 +169,7 @@ class Owners { // First, try to read from the legacy file. final File legacy = getLegacyConfigFile(); - final List<UserInfo> users = mUserManager.getUsers(true); + final List<UserInfo> users = mUserManager.getAliveUsers(); if (readLegacyOwnerFileLocked(legacy)) { if (DEBUG) { diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index 41945a276fde..87ae4d719d11 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -237,6 +237,13 @@ binder::Status BinderIncrementalService::unlink(int32_t storageId, const std::st return ok(); } +binder::Status BinderIncrementalService::isFileFullyLoaded(int32_t storageId, + const std::string& path, + int32_t* _aidl_return) { + *_aidl_return = mImpl.isFileFullyLoaded(storageId, path); + return ok(); +} + binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId, float* _aidl_return) { *_aidl_return = mImpl.getLoadingProgress(storageId); diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 8b40350468ce..8478142b2d95 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -66,6 +66,8 @@ public: int32_t destStorageId, const std::string& destPath, int32_t* _aidl_return) final; binder::Status unlink(int32_t storageId, const std::string& path, int32_t* _aidl_return) final; + binder::Status isFileFullyLoaded(int32_t storageId, const std::string& path, + int32_t* _aidl_return) final; binder::Status getLoadingProgress(int32_t storageId, float* _aidl_return) final; binder::Status getMetadataByPath(int32_t storageId, const std::string& path, std::vector<uint8_t>* _aidl_return) final; diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 9836262ec2b0..447ee552a335 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -1603,7 +1603,8 @@ void IncrementalService::extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId); if (!writeFd.ok()) { - LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd; + LOG(ERROR) << "Failed to open write fd for: " << targetLibPath + << " errno: " << writeFd.get(); return; } @@ -1673,6 +1674,37 @@ bool IncrementalService::waitForNativeBinariesExtraction(StorageId storage) { return mRunning; } +int IncrementalService::isFileFullyLoaded(StorageId storage, const std::string& path) const { + std::unique_lock l(mLock); + const auto ifs = getIfsLocked(storage); + if (!ifs) { + LOG(ERROR) << "isFileFullyLoaded failed, invalid storageId: " << storage; + return -EINVAL; + } + const auto storageInfo = ifs->storages.find(storage); + if (storageInfo == ifs->storages.end()) { + LOG(ERROR) << "isFileFullyLoaded failed, no storage: " << storage; + return -EINVAL; + } + l.unlock(); + return isFileFullyLoadedFromPath(*ifs, path); +} + +int IncrementalService::isFileFullyLoadedFromPath(const IncFsMount& ifs, + std::string_view filePath) const { + const auto [filledBlocks, totalBlocks] = mIncFs->countFilledBlocks(ifs.control, filePath); + if (filledBlocks < 0) { + LOG(ERROR) << "isFileFullyLoadedFromPath failed to get filled blocks count for: " + << filePath << " errno: " << filledBlocks; + return filledBlocks; + } + if (totalBlocks < filledBlocks) { + LOG(ERROR) << "isFileFullyLoadedFromPath failed to get total num of blocks"; + return -EINVAL; + } + return totalBlocks - filledBlocks; +} + float IncrementalService::getLoadingProgress(StorageId storage) const { std::unique_lock l(mLock); const auto ifs = getIfsLocked(storage); diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index cd6bfedb8a9e..267458d8769c 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -132,6 +132,7 @@ public: std::string_view newPath); int unlink(StorageId storage, std::string_view path); + int isFileFullyLoaded(StorageId storage, const std::string& path) const; float getLoadingProgress(StorageId storage) const; RawMetadata getMetadata(StorageId storage, std::string_view path) const; @@ -339,6 +340,7 @@ private: int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode); binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); + int isFileFullyLoadedFromPath(const IncFsMount& ifs, std::string_view filePath) const; float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const; void registerAppOpsCallback(const std::string& packageName); diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index 1ed46c49c5e1..f6d89c53be32 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -195,8 +195,8 @@ public: ErrorCode unlink(const Control& control, std::string_view path) const final { return incfs::unlink(control, path); } - base::unique_fd openForSpecialOps(const Control& control, FileId id) const final { - return base::unique_fd{incfs::openForSpecialOps(control, id).release()}; + incfs::UniqueFd openForSpecialOps(const Control& control, FileId id) const final { + return incfs::openForSpecialOps(control, id); } ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final { return incfs::writeBlocks({blocks.data(), size_t(blocks.size())}); diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index 82a170470fee..6376d86543f8 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -74,6 +74,7 @@ public: using Control = incfs::Control; using FileId = incfs::FileId; using ErrorCode = incfs::ErrorCode; + using UniqueFd = incfs::UniqueFd; using WaitResult = incfs::WaitResult; using ExistingMountCallback = @@ -96,7 +97,7 @@ public: virtual ErrorCode link(const Control& control, std::string_view from, std::string_view to) const = 0; virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0; - virtual base::unique_fd openForSpecialOps(const Control& control, FileId id) const = 0; + virtual UniqueFd openForSpecialOps(const Control& control, FileId id) const = 0; virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0; virtual WaitResult waitForPendingReads( const Control& control, std::chrono::milliseconds timeout, diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index d1000e56e5ee..a290a1791481 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -289,7 +289,7 @@ public: ErrorCode(const Control& control, std::string_view from, std::string_view to)); MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path)); - MOCK_CONST_METHOD2(openForSpecialOps, base::unique_fd(const Control& control, FileId id)); + MOCK_CONST_METHOD2(openForSpecialOps, UniqueFd(const Control& control, FileId id)); MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks)); MOCK_CONST_METHOD3(waitForPendingReads, WaitResult(const Control& control, std::chrono::milliseconds timeout, @@ -304,6 +304,10 @@ public: ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(1, 2))); } + void countFilledBlocksFullyLoaded() { + ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(10000, 10000))); + } + void countFilledBlocksFails() { ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(-1, -1))); } @@ -1069,6 +1073,53 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) { ASSERT_EQ(res, 0); } +TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithNoFile) { + mIncFs->countFilledBlocksFails(); + mFs->hasNoFile(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}); + ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); +} + +TEST_F(IncrementalServiceTest, testIsFileFullyLoadedFailsWithFailedRanges) { + mIncFs->countFilledBlocksFails(); + mFs->hasFiles(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}); + EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); + ASSERT_EQ(-1, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); +} + +TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccessWithEmptyRanges) { + mIncFs->countFilledBlocksEmpty(); + mFs->hasFiles(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}); + EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); + ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); +} + +TEST_F(IncrementalServiceTest, testIsFileFullyLoadedSuccess) { + mIncFs->countFilledBlocksFullyLoaded(); + mFs->hasFiles(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, {}, {}); + EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1); + ASSERT_EQ(0, mIncrementalService->isFileFullyLoaded(storageId, "base.apk")); +} + TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) { mIncFs->countFilledBlocksSuccess(); mFs->hasNoFile(); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index b306ff091267..431cc27a6635 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -16,7 +16,6 @@ package com.android.server.devicepolicy; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; @@ -236,7 +235,7 @@ public class MockSystemServices { } mUserInfos.add(uh); when(userManager.getUsers()).thenReturn(mUserInfos); - when(userManager.getUsers(anyBoolean())).thenReturn(mUserInfos); + when(userManager.getAliveUsers()).thenReturn(mUserInfos); when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(true); when(userManager.getProfileParent(anyInt())).thenAnswer( invocation -> { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 99433a6603c9..d7e431f3bb51 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -978,6 +978,7 @@ public class ManagedServicesTest extends UiServiceTestCase { assertFalse(services.isSameUser(service, 0)); assertTrue(services.isSameUser(service, 10)); + assertTrue(services.isSameUser(service, UserHandle.USER_ALL)); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index ab4dc476ff20..5796e848ff6e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -103,7 +103,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { when(mUm.getUserInfo(eq(user.id))).thenReturn(user); } when(mUm.getUsers()).thenReturn(users); - when(mUm.getUsers(anyBoolean())).thenReturn(users); + when(mUm.getAliveUsers()).thenReturn(users); IntArray profileIds = new IntArray(); profileIds.add(0); profileIds.add(11); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 9319bea497fb..86447192a441 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -5058,7 +5058,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 10, 10, r.getKey(), actionIndex, action, notificationVisibility, generatedByAssistant); verify(mAssistants).notifyAssistantActionClicked( - eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant)); + eq(r.getSbn()), eq(action), eq(generatedByAssistant)); assertEquals(1, mNotificationRecordLogger.numCalls()); assertEquals( @@ -5082,7 +5082,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { 10, 10, r.getKey(), actionIndex, action, notificationVisibility, generatedByAssistant); verify(mAssistants).notifyAssistantActionClicked( - eq(r.getSbn()), eq(actionIndex), eq(action), eq(generatedByAssistant)); + eq(r.getSbn()), eq(action), eq(generatedByAssistant)); assertEquals(1, mNotificationRecordLogger.numCalls()); assertEquals( @@ -6948,4 +6948,63 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(NotificationManagerService.MAX_PACKAGE_NOTIFICATIONS + 1, mService.getNotificationRecordCount()); } + + @Test + public void testIsVisibleToListener_notEnabled() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getUserId()).thenReturn(10); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class); + info.userid = 10; + when(info.isSameUser(anyInt())).thenReturn(true); + when(assistant.isSameUser(anyInt())).thenReturn(true); + when(info.enabledAndUserMatches(info.userid)).thenReturn(false); + when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant); + + assertFalse(mService.isVisibleToListener(sbn, info)); + } + + @Test + public void testIsVisibleToListener_noAssistant() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getUserId()).thenReturn(10); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + info.userid = 10; + when(info.isSameUser(anyInt())).thenReturn(true); + when(info.enabledAndUserMatches(info.userid)).thenReturn(true); + when(mAssistants.checkServiceTokenLocked(any())).thenReturn(null); + + assertTrue(mService.isVisibleToListener(sbn, info)); + } + + @Test + public void testIsVisibleToListener_assistant_differentUser() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getUserId()).thenReturn(10); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class); + info.userid = 0; + when(info.isSameUser(anyInt())).thenReturn(true); + when(assistant.isSameUser(anyInt())).thenReturn(true); + when(info.enabledAndUserMatches(info.userid)).thenReturn(true); + when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant); + + assertFalse(mService.isVisibleToListener(sbn, info)); + } + + @Test + public void testIsVisibleToListener_assistant_sameUser() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + when(sbn.getUserId()).thenReturn(10); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class); + info.userid = 10; + when(info.isSameUser(anyInt())).thenReturn(true); + when(assistant.isSameUser(anyInt())).thenReturn(true); + when(info.enabledAndUserMatches(info.userid)).thenReturn(true); + when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant); + + assertTrue(mService.isVisibleToListener(sbn, info)); + } + } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index e2948a724acd..4cad39762a7b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -1202,19 +1202,22 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testShouldSleepActivities() { // When focused activity and keyguard is going away, we should not sleep regardless - // of the display state + // of the display state, but keyguard-going-away should only take effects on default + // display since there is no keyguard on secondary displays (yet). verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, - true /* displaySleeping */, false /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); + verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/, + true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */); // When not the focused stack, defer to display sleeping state. verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/, - true /* displaySleeping */, true /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); // If keyguard is going away, defer to the display sleeping state. verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, - true /* displaySleeping */, true /* expected*/); + true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */); verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/, - false /* displaySleeping */, false /* expected*/); + false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */); } @Test @@ -1423,9 +1426,11 @@ public class ActivityStackTests extends WindowTestsBase { } private void verifyShouldSleepActivities(boolean focusedStack, - boolean keyguardGoingAway, boolean displaySleeping, boolean expected) { + boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay, + boolean expected) { final DisplayContent display = mock(DisplayContent.class); final KeyguardController keyguardController = mSupervisor.getKeyguardController(); + display.isDefaultDisplay = isDefaultDisplay; doReturn(display).when(mStack).getDisplay(); doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway(); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index b89d16807a6e..26b0bfb1dd7c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -915,24 +915,6 @@ public class RootActivityContainerTests extends WindowTestsBase { assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask()); } - @Test - public void testResumeFocusedStackOnSleepingDisplay() { - // Create an activity on secondary display. - final TestDisplayContent secondDisplay = addNewDisplayContentAt( - DisplayContent.POSITION_TOP); - final Task stack = secondDisplay.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final ActivityRecord activity = new ActivityBuilder(mAtm).setStack(stack).build(); - spyOn(activity); - spyOn(stack); - - // Cannot resumed activities on secondary display if the display should sleep. - doReturn(true).when(secondDisplay).shouldSleep(); - mRootWindowContainer.resumeFocusedStacksTopActivities(); - verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any()); - verify(activity, never()).makeActiveIfNeeded(any()); - } - /** * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity * info for test cases. diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 289d54e967f5..46a6a82faba5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -930,23 +930,36 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task stack = createStack(); final Task task = createTask(stack); final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task); + final Task stack2 = createStack(); + final Task task2 = createTask(stack2); + final ActivityRecord activity2 = createActivityRecordInTask(stack.mDisplayContent, task2); final ITaskOrganizer organizer = registerMockOrganizer(); // Setup the task to be controlled by the MW mode organizer stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); assertTrue(stack.isOrganized()); + assertTrue(stack2.isOrganized()); // Verify a back pressed does not call the organizer mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); verify(organizer, never()).onBackPressedOnTaskRoot(any()); // Enable intercepting back - mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot(organizer, - true); + mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( + stack.mRemoteToken.toWindowContainerToken(), true); // Verify now that the back press does call the organizer mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); + + // Disable intercepting back + mWm.mAtmService.mTaskOrganizerController.setInterceptBackPressedOnTaskRoot( + stack.mRemoteToken.toWindowContainerToken(), false); + + // Verify now that the back press no longer calls the organizer + mWm.mAtmService.onBackPressedOnTaskRoot(activity.token); + verify(organizer, times(1)).onBackPressedOnTaskRoot(any()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 9603d28c286b..3106ca26c8a1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -661,14 +661,14 @@ public class WindowStateTests extends WindowTestsBase { RecentsAnimationController recentsController = mock(RecentsAnimationController.class); when(recentsController.shouldApplyInputConsumer(win0.mActivityRecord)).thenReturn(true); mWm.setRecentsAnimationController(recentsController); - assertTrue(win0.cantReceiveTouchInput()); + assertFalse(win0.canReceiveTouchInput()); } @Test public void testCantReceiveTouchWhenAppTokenHiddenRequested() { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); win0.mActivityRecord.mVisibleRequested = false; - assertTrue(win0.cantReceiveTouchInput()); + assertFalse(win0.canReceiveTouchInput()); } @Test @@ -676,7 +676,7 @@ public class WindowStateTests extends WindowTestsBase { final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); win0.mActivityRecord.getStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); win0.mActivityRecord.getStack().setFocusable(false); - assertTrue(win0.cantReceiveTouchInput()); + assertFalse(win0.canReceiveTouchInput()); } @UseTestDisplay(addWindows = W_ACTIVITY) diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 81aad972898e..f151d9ca2420 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -183,6 +183,7 @@ public class UsageStatsService extends SystemService implements private static class ActivityData { private final String mTaskRootPackage; private final String mTaskRootClass; + public int lastEvent = Event.NONE; private ActivityData(String taskRootPackage, String taskRootClass) { mTaskRootPackage = taskRootPackage; mTaskRootClass = taskRootClass; @@ -785,6 +786,7 @@ public class UsageStatsService extends SystemService implements switch (event.mEventType) { case Event.ACTIVITY_RESUMED: case Event.ACTIVITY_PAUSED: + case Event.ACTIVITY_STOPPED: uid = mPackageManagerInternal.getPackageUid(event.mPackage, 0, userId); break; default: @@ -817,8 +819,10 @@ public class UsageStatsService extends SystemService implements .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_FOREGROUND); // check if this activity has already been resumed if (mVisibleActivities.get(event.mInstanceId) != null) break; - mVisibleActivities.put(event.mInstanceId, - new ActivityData(event.mTaskRootPackage, event.mTaskRootClass)); + final ActivityData resumedData = new ActivityData(event.mTaskRootPackage, + event.mTaskRootClass); + resumedData.lastEvent = Event.ACTIVITY_RESUMED; + mVisibleActivities.put(event.mInstanceId, resumedData); try { switch(mUsageSource) { case USAGE_SOURCE_CURRENT_ACTIVITY: @@ -834,16 +838,17 @@ public class UsageStatsService extends SystemService implements } break; case Event.ACTIVITY_PAUSED: - if (event.mTaskRootPackage == null) { - // Task Root info is missing. Repair the event based on previous data - final ActivityData prevData = mVisibleActivities.get(event.mInstanceId); - if (prevData == null) { - Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage - + "/" + event.mClass + " event : " + event.mEventType - + " instanceId : " + event.mInstanceId + ")"); - } else { - event.mTaskRootPackage = prevData.mTaskRootPackage; - event.mTaskRootClass = prevData.mTaskRootClass; + final ActivityData pausedData = mVisibleActivities.get(event.mInstanceId); + if (pausedData == null) { + Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage + + "/" + event.mClass + " event : " + event.mEventType + + " instanceId : " + event.mInstanceId + ")"); + } else { + pausedData.lastEvent = Event.ACTIVITY_PAUSED; + if (event.mTaskRootPackage == null) { + // Task Root info is missing. Repair the event based on previous data + event.mTaskRootPackage = pausedData.mTaskRootPackage; + event.mTaskRootClass = pausedData.mTaskRootClass; } } FrameworkStatsLog.write( @@ -866,6 +871,16 @@ public class UsageStatsService extends SystemService implements return; } + if (prevData.lastEvent != Event.ACTIVITY_PAUSED) { + FrameworkStatsLog.write( + FrameworkStatsLog.APP_USAGE_EVENT_OCCURRED, + uid, + event.mPackage, + event.mClass, + FrameworkStatsLog + .APP_USAGE_EVENT_OCCURRED__EVENT_TYPE__MOVE_TO_BACKGROUND); + } + ArraySet<String> tokens; synchronized (mUsageReporters) { tokens = mUsageReporters.removeReturnOld(event.mInstanceId); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a229efbe9970..470d4bec4c38 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1678,6 +1678,15 @@ public class CarrierConfigManager { "hide_lte_plus_data_icon_bool"; /** + * The combined channel bandwidth threshold (non-inclusive) in KHz required to display the + * LTE+ data icon. It is 20000 by default, meaning the LTE+ icon will be shown if the device is + * using carrier aggregation and the combined channel bandwidth is strictly greater than 20 MHz. + * @hide + */ + public static final String KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT = + "lte_plus_threshold_bandwidth_khz_int"; + + /** * The string is used to filter redundant string from PLMN Network Name that's supplied by * specific carrier. * @@ -4259,6 +4268,7 @@ public class CarrierConfigManager { sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, ""); sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, ""); sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true); + sDefaults.putInt(KEY_LTE_PLUS_THRESHOLD_BANDWIDTH_KHZ_INT, 20000); sDefaults.putBoolean(KEY_NR_ENABLED_BOOL, true); sDefaults.putBoolean(KEY_LTE_ENABLED_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_TDSCDMA_BOOL, false); diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 4f5a30502c91..7dd003eb9755 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -15,6 +15,7 @@ android_test { name: "RollbackTest", manifest: "RollbackTest/AndroidManifest.xml", + platform_apis: true, srcs: ["RollbackTest/src/**/*.java"], static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"], test_suites: ["general-tests"], diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index de51c5ca19ed..0db2b2af7260 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -175,7 +175,7 @@ public class RollbackTest { assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1); UserManager um = (UserManager) context.getSystemService(context.USER_SERVICE); - List<Integer> userIds = um.getUsers(true) + List<Integer> userIds = um.getAliveUsers() .stream().map(user -> user.id).collect(Collectors.toList()); assertThat(InstallUtils.isOnlyInstalledForUser(TestApp.A, context.getUserId(), userIds)).isTrue(); diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index aa3a13925894..9302f78b7fca 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -81,6 +81,12 @@ public final class ScanResult implements Parcelable { public String capabilities; /** + * The interface name on which the scan result was received. + * @hide + */ + public String ifaceName; + + /** * @hide * No security protocol. */ @@ -939,6 +945,7 @@ public final class ScanResult implements Parcelable { flags = source.flags; radioChainInfos = source.radioChainInfos; this.mWifiStandard = source.mWifiStandard; + this.ifaceName = source.ifaceName; } } @@ -977,6 +984,7 @@ public final class ScanResult implements Parcelable { sb.append(", 80211mcResponder: "); sb.append(((flags & FLAG_80211mc_RESPONDER) != 0) ? "is supported" : "is not supported"); sb.append(", Radio Chain Infos: ").append(Arrays.toString(radioChainInfos)); + sb.append(", interface name: ").append(ifaceName); return sb.toString(); } @@ -1056,6 +1064,7 @@ public final class ScanResult implements Parcelable { } else { dest.writeInt(0); } + dest.writeString((ifaceName != null) ? ifaceName.toString() : ""); } /** Implement the Parcelable interface */ @@ -1134,6 +1143,7 @@ public final class ScanResult implements Parcelable { sr.radioChainInfos[i].level = in.readInt(); } } + sr.ifaceName = in.readString(); return sr; } diff --git a/wifi/tests/src/android/net/wifi/ScanResultTest.java b/wifi/tests/src/android/net/wifi/ScanResultTest.java index 5516f433070f..4a3586826de9 100644 --- a/wifi/tests/src/android/net/wifi/ScanResultTest.java +++ b/wifi/tests/src/android/net/wifi/ScanResultTest.java @@ -44,6 +44,7 @@ public class ScanResultTest { public static final long TEST_TSF = 04660l; public static final @WifiAnnotations.WifiStandard int TEST_WIFI_STANDARD = ScanResult.WIFI_STANDARD_11AC; + public static final String TEST_IFACE_NAME = "test_ifname"; /** * Frequency to channel map. This include some frequencies used outside the US. @@ -219,7 +220,7 @@ public class ScanResultTest { + "passpoint: no, ChannelBandwidth: 0, centerFreq0: 0, centerFreq1: 0, " + "standard: 11ac, " + "80211mcResponder: is not supported, " - + "Radio Chain Infos: null", scanResult.toString()); + + "Radio Chain Infos: null, interface name: test_ifname", scanResult.toString()); } /** @@ -242,7 +243,8 @@ public class ScanResultTest { + "standard: 11ac, " + "80211mcResponder: is not supported, " + "Radio Chain Infos: [RadioChainInfo: id=0, level=-45, " - + "RadioChainInfo: id=1, level=-54]", scanResult.toString()); + + "RadioChainInfo: id=1, level=-54], interface name: test_ifname", + scanResult.toString()); } /** @@ -283,6 +285,8 @@ public class ScanResultTest { result.frequency = TEST_FREQUENCY; result.timestamp = TEST_TSF; result.setWifiStandard(TEST_WIFI_STANDARD); + result.ifaceName = TEST_IFACE_NAME; + return result; } |