diff options
130 files changed, 2840 insertions, 1887 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index 2bd5aee0cd24..bb6538739c49 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -299,6 +299,7 @@ java_defaults { static_libs: [ // License notices from art module "art-notices-for-framework-stubs-jar", + "framework-res-package-jar", // Export package of framework-res ], errorprone: { javacflags: [ @@ -311,6 +312,15 @@ java_defaults { compile_dex: true, } +java_defaults { + name: "android_stubs_dists_default", + dist: { + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android.jar", + }, +} + java_library_static { name: "android_monolith_stubs_current", srcs: [ ":api-stubs-docs" ], @@ -346,7 +356,21 @@ java_library_static { name: "android_system_monolith_stubs_current", srcs: [ ":system-api-stubs-docs" ], static_libs: [ "private-stub-annotations-jar" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/system", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_system.jar", + }, + ], } java_library_static { @@ -378,14 +402,34 @@ java_library_static { name: "android_test_stubs_current", srcs: [ ":test-api-stubs-docs" ], static_libs: [ "private-stub-annotations-jar" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/test", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_test.jar", + }, + ], } java_library_static { name: "android_module_lib_stubs_current", srcs: [ ":module-lib-api-stubs-docs-non-updatable" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], libs: ["sdk_system_29_android"], + dist: { + dir: "apistubs/android/module-lib", + }, } java_library_static { diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml index c7929af6077f..fbe589248338 100644 --- a/apct-tests/perftests/multiuser/AndroidTest.xml +++ b/apct-tests/perftests/multiuser/AndroidTest.xml @@ -27,6 +27,9 @@ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config_detailed.textproto" value="/sdcard/sample.textproto" /> </target_preparer> <!-- Needed for pulling the collected trace config on to the host --> diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 6b335ee1b923..8b2c2da9396d 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -485,6 +485,9 @@ message Atom { NetworkTetheringReported network_tethering_reported = 303 [(module) = "network_tethering"]; ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"]; + UIInteractionFrameInfoReported ui_interaction_frame_info_reported = + 305 [(module) = "framework"]; + UIActionLatencyReported ui_action_latency_reported = 306 [(module) = "framework"]; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. @@ -5054,6 +5057,54 @@ message BlobOpened{ optional Result result = 4; } +/** + * Event to track Jank for various system interactions. + * + * Logged from: + * frameworks/base/core/java/android/os/aot/FrameTracker.java + */ +message UIInteractionFrameInfoReported { + enum InteractionType { + UNKNOWN = 0; + NOTIFICATION_SHADE_SWIPE = 1; + } + + optional InteractionType interaction_type = 1; + + // Number of frames rendered during the interaction. + optional int64 total_frames = 2; + + // Number of frames that were skipped in rendering during the interaction. + optional int64 missed_frames = 3; + + // Maximum time it took to render a single frame during the interaction. + optional int64 max_frame_time_nanos = 4; +} + +/** + * Event to track various latencies in SystemUI. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/util/LatencyTracker.java + */ +message UIActionLatencyReported { + enum ActionType { + UNKNOWN = 0; + ACTION_EXPAND_PANEL = 1; + ACTION_TOGGLE_RECENTS = 2; + ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 3; + ACTION_CHECK_CREDENTIAL = 4; + ACTION_CHECK_CREDENTIAL_UNLOCKED = 5; + ACTION_TURN_ON_SCREEN = 6; + ACTION_ROTATE_SCREEN = 7; + ACTION_FACE_WAKE_AND_UNLOCK = 8; + } + + optional ActionType action = 1; + + optional int64 latency_millis = 2; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index 7a1ece94fd3e..3b65f8225ee9 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -38,9 +38,23 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf const vector<sp<ConditionTracker>>& allConditionTrackers, const unordered_map<int64_t, int>& conditionIdIndexMap, vector<bool>& stack, - vector<ConditionState>& initialConditionCache) { + vector<ConditionState>& conditionCache) { VLOG("Combination predicate init() %lld", (long long)mConditionId); if (mInitialized) { + // All the children are guaranteed to be initialized, but the recursion is needed to + // fill the conditionCache properly, since another combination condition or metric + // might rely on this. The recursion is needed to compute the current condition. + + // Init is called instead of isConditionMet so that the ConditionKey can be filled with the + // default key for sliced conditions, since we do not know all indirect descendants here. + for (const int childIndex : mChildren) { + if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { + allConditionTrackers[childIndex]->init(allConditionConfig, allConditionTrackers, + conditionIdIndexMap, stack, conditionCache); + } + } + conditionCache[mIndex] = + evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); return true; } @@ -74,9 +88,8 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf return false; } - bool initChildSucceeded = - childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap, - stack, initialConditionCache); + bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers, + conditionIdIndexMap, stack, conditionCache); if (!initChildSucceeded) { ALOGW("Child initialization failed %lld ", (long long)child); @@ -96,10 +109,10 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf childTracker->getAtomMatchingTrackerIndex().end()); } - mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, - initialConditionCache); - initialConditionCache[mIndex] = - evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache); + mUnSlicedPartCondition = + evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache); + conditionCache[mIndex] = + evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); // unmark this node in the recursion stack. stack[mIndex] = false; diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 39ff0ab03266..a7fac3deaabe 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -33,7 +33,7 @@ public: bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, - std::vector<ConditionState>& initialConditionCache) override; + std::vector<ConditionState>& conditionCache) override; void evaluateCondition(const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 9da1af427e5f..4e1253506be7 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -46,17 +46,19 @@ public: // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also // be done in the constructor, but we do it separately because (1) easy to return a bool to // indicate whether the initialization is successful. (2) makes unit test easier. + // This function can also be called on config updates, in which case it does nothing other than + // fill the condition cache with the current condition. // allConditionConfig: the list of all Predicate config from statsd_config. // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also // need to call init() on children conditions) // conditionIdIndexMap: the mapping from condition id to its index. // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. - // initialConditionCache: tracks initial conditions of all ConditionTrackers. + // conditionCache: tracks initial conditions of all ConditionTrackers. returns the + // current condition if called on a config update. virtual bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, const std::unordered_map<int64_t, int>& conditionIdIndexMap, - std::vector<bool>& stack, - std::vector<ConditionState>& initialConditionCache) = 0; + std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0; // evaluate current condition given the new event. // event: the new log event diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index efb4d4989425..f45759b6a77e 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -95,11 +95,14 @@ SimpleConditionTracker::~SimpleConditionTracker() { bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig, const vector<sp<ConditionTracker>>& allConditionTrackers, const unordered_map<int64_t, int>& conditionIdIndexMap, - vector<bool>& stack, - vector<ConditionState>& initialConditionCache) { + vector<bool>& stack, vector<ConditionState>& conditionCache) { // SimpleConditionTracker does not have dependency on other conditions, thus we just return // if the initialization was successful. - initialConditionCache[mIndex] = mInitialValue; + ConditionKey conditionKey; + if (mSliced) { + conditionKey[mConditionId] = DEFAULT_DIMENSION_KEY; + } + isConditionMet(conditionKey, allConditionTrackers, mSliced, conditionCache); return mInitialized; } diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index ea7f87bde2b8..1a9e35e38207 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -38,7 +38,7 @@ public: bool init(const std::vector<Predicate>& allConditionConfig, const std::vector<sp<ConditionTracker>>& allConditionTrackers, const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack, - std::vector<ConditionState>& initialConditionCache) override; + std::vector<ConditionState>& conditionCache) override; void evaluateCondition(const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues, diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index daf67e93557c..e40fbdb250f1 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -303,8 +303,7 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, const int conditionTrackerCount = config.predicate_size(); conditionConfigs.reserve(conditionTrackerCount); allConditionTrackers.reserve(conditionTrackerCount); - initialConditionCache.reserve(conditionTrackerCount); - std::fill(initialConditionCache.begin(), initialConditionCache.end(), ConditionState::kUnknown); + initialConditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated); for (int i = 0; i < conditionTrackerCount; i++) { const Predicate& condition = config.predicate(i); diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 1eb3fc11df7b..df1f1b21eba3 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -68,7 +68,7 @@ public class GraphicsEnvironment { private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1"; private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time"; private static final String METADATA_DRIVER_BUILD_TIME = - "com.android.graphics.updatabledriver.build_time"; + "com.android.graphics.driver.build_time"; private static final String METADATA_DEVELOPER_DRIVER_ENABLE = "com.android.graphics.developerdriver.enable"; private static final String METADATA_INJECT_LAYERS_ENABLE = @@ -878,9 +878,10 @@ public class GraphicsEnvironment { throw new NullPointerException("apk's meta-data cannot be null"); } - final String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME); - if (driverBuildTime == null || driverBuildTime.isEmpty()) { - Log.v(TAG, "com.android.graphics.updatabledriver.build_time is not set"); + String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME); + if (driverBuildTime == null || driverBuildTime.length() <= 1) { + Log.v(TAG, "com.android.graphics.driver.build_time is not set"); + driverBuildTime = "L0"; } // driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456. // Long.parseLong will throw if the meta-data "driver_build_time" is not set properly. diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 50cc764dd536..58c8efa3a972 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -102,6 +102,8 @@ public final class Trace { /** @hide */ public static final long TRACE_TAG_RRO = 1L << 26; /** @hide */ + public static final long TRACE_TAG_SYSPROP = 1L << 27; + /** @hide */ public static final long TRACE_TAG_APEX_MANAGER = 1L << 18; private static final long TRACE_TAG_NOT_READY = 1L << 63; diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 7f45c044408a..403ac3ab29c0 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -629,7 +629,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); } - if (!mState.equals(state, true /* excludingCaptionInsets */, + if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */, true /* excludeInvisibleIme */)) { if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState); updateRequestedState(); @@ -1138,15 +1138,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (invokeCallback) { control.cancel(); } + boolean stateChanged = false; for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); if (runningAnimation.runner == control) { mRunningAnimations.remove(i); ArraySet<Integer> types = toInternalType(control.getTypes()); for (int j = types.size() - 1; j >= 0; j--) { - if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) { - mHost.notifyInsetsChanged(); - } + stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); } if (invokeCallback && runningAnimation.startDispatched) { dispatchAnimationEnd(runningAnimation.runner.getAnimation()); @@ -1154,6 +1153,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation break; } } + if (stateChanged) { + mHost.notifyInsetsChanged(); + updateRequestedState(); + } } private void applyLocalVisibilityOverride() { diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 6b0b509932a8..593b37af26ad 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -60,6 +60,8 @@ import java.util.StringJoiner; */ public class InsetsState implements Parcelable { + public static final InsetsState EMPTY = new InsetsState(); + /** * Internal representation of inset source types. This is different from the public API in * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 053b06f3d407..2f8c45770eb5 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -347,7 +347,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { super(context); mLayoutInflater = LayoutInflater.from(context); mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(), - DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0; + DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; } /** diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index c82a6564c1d7..937b9426476a 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -200,6 +200,20 @@ public class Preconditions { } /** + * Ensures the truth of an expression involving whether the calling identity is authorized to + * call the calling method. + * + * @param expression a boolean expression + * @param message the message of the security exception to be thrown + * @throws SecurityException if {@code expression} is false + */ + public static void checkSecurity(final boolean expression, final String message) { + if (!expression) { + throw new SecurityException(message); + } + } + + /** * Ensures the truth of an expression involving whether the calling user is authorized to * call the calling method. * diff --git a/core/res/Android.bp b/core/res/Android.bp index b365de4f4630..f94a2b08e6c3 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -46,6 +46,13 @@ android_app { }, } +java_genrule { + name: "framework-res-package-jar", + srcs: [":framework-res{.export-package.apk}"], + out: ["framework-res-package.jar"], + cmd: "cp $(in) $(out)", +} + // This logic can be removed once robolectric's transition to binary resources is complete filegroup { name: "robolectric_framework_raw_res_files", diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 32c1e4a1411c..b16d4b264e4f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5040,6 +5040,10 @@ <permission android:name="android.permission.RESET_APP_ERRORS" android:protectionLevel="signature" /> + <!-- @hide Allows an application to create/destroy input consumer. --> + <permission android:name="android.permission.INPUT_CONSUMER" + android:protectionLevel="signature" /> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index af02b7bdbd90..de128ad6d78e 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -746,6 +746,20 @@ public class InsetsControllerTest { mController.onControlsChanged(createSingletonControl(ITYPE_IME)); assertEquals(newState.getSource(ITYPE_IME), mTestHost.getModifiedState().peekSource(ITYPE_IME)); + + // The modified frames cannot be updated if there is an animation. + mController.onControlsChanged(createSingletonControl(ITYPE_NAVIGATION_BAR)); + mController.hide(navigationBars()); + newState = new InsetsState(mController.getState(), true /* copySource */); + newState.getSource(ITYPE_NAVIGATION_BAR).getFrame().top--; + mController.onStateChanged(newState); + assertNotEquals(newState.getSource(ITYPE_NAVIGATION_BAR), + mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR)); + + // The modified frames can be updated while the animation is done. + mController.cancelExistingAnimations(); + assertEquals(newState.getSource(ITYPE_NAVIGATION_BAR), + mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR)); }); } diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index d0e688d3efc1..e122e0026aac 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -144,13 +144,6 @@ prebuilt_etc { } prebuilt_etc { - name: "allowed_privapp_com.android.car.floatingcardslauncher", - sub_dir: "permissions", - src: "com.android.car.floatingcardslauncher.xml", - filename_from_src: true, -} - -prebuilt_etc { name: "allowed_privapp_com.android.car.ui.paintbooth", sub_dir: "permissions", src: "com.android.car.ui.paintbooth.xml", diff --git a/data/etc/car/com.android.car.floatingcardslauncher.xml b/data/etc/car/com.android.car.floatingcardslauncher.xml deleted file mode 100644 index 2755fee4eb55..000000000000 --- a/data/etc/car/com.android.car.floatingcardslauncher.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ 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 - --> -<permissions> - <privapp-permissions package="com.android.car.floatingcardslauncher"> - <permission name="android.permission.ACTIVITY_EMBEDDING"/> - <permission name="android.permission.INTERACT_ACROSS_USERS"/> - <permission name="android.permission.MANAGE_USERS"/> - <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> - <permission name="android.permission.MODIFY_PHONE_STATE"/> - </privapp-permissions> -</permissions> diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 4d7e5dfea4f7..dfb4009b07e2 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -37,7 +37,6 @@ #include <androidfw/TypeWrappers.h> #include <cutils/atomic.h> #include <utils/ByteOrder.h> -#include <utils/Debug.h> #include <utils/Log.h> #include <utils/String16.h> #include <utils/String8.h> diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index 87244427a719..ab9b8b55a4cb 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -56,12 +56,6 @@ class AHBUploader : public RefBase { public: virtual ~AHBUploader() {} - // Called to start creation of the Vulkan and EGL contexts on another thread before we actually - // need to do an upload. - void initialize() { - onInitialize(); - } - void destroy() { std::lock_guard _lock{mLock}; LOG_ALWAYS_FATAL_IF(mPendingUploads, "terminate called while uploads in progress"); @@ -91,7 +85,6 @@ protected: sp<ThreadBase> mUploadThread = nullptr; private: - virtual void onInitialize() = 0; virtual void onIdle() = 0; virtual void onDestroy() = 0; @@ -141,7 +134,6 @@ private: class EGLUploader : public AHBUploader { private: - void onInitialize() override {} void onDestroy() override { mEglManager.destroy(); } @@ -231,62 +223,67 @@ private: class VkUploader : public AHBUploader { private: - void onInitialize() override { - std::lock_guard _lock{mLock}; - if (!mUploadThread) { - mUploadThread = new ThreadBase{}; - } - if (!mUploadThread->isRunning()) { - mUploadThread->start("GrallocUploadThread"); - } - - mUploadThread->queue().post([this]() { - std::lock_guard _lock{mVkLock}; - if (!mVulkanManager.hasVkContext()) { - mVulkanManager.initialize(); - } - }); - } void onDestroy() override { + std::lock_guard _lock{mVkLock}; mGrContext.reset(); - mVulkanManager.destroy(); + mVulkanManagerStrong.clear(); } void onIdle() override { - mGrContext.reset(); + onDestroy(); } - void onBeginUpload() override { - { - std::lock_guard _lock{mVkLock}; - if (!mVulkanManager.hasVkContext()) { - LOG_ALWAYS_FATAL_IF(mGrContext, - "GrContext exists with no VulkanManager for vulkan uploads"); - mUploadThread->queue().runSync([this]() { - mVulkanManager.initialize(); - }); - } - } - if (!mGrContext) { - GrContextOptions options; - mGrContext = mVulkanManager.createContext(options); - LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads"); - this->postIdleTimeoutCheck(); - } - } + void onBeginUpload() override {} bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, AHardwareBuffer* ahb) override { - ATRACE_CALL(); + bool uploadSucceeded = false; + mUploadThread->queue().runSync([this, &uploadSucceeded, bitmap, ahb]() { + ATRACE_CALL(); + std::lock_guard _lock{mVkLock}; + + renderthread::VulkanManager* vkManager = getVulkanManager(); + if (!vkManager->hasVkContext()) { + LOG_ALWAYS_FATAL_IF(mGrContext, + "GrContext exists with no VulkanManager for vulkan uploads"); + vkManager->initialize(); + } + + if (!mGrContext) { + GrContextOptions options; + mGrContext = vkManager->createContext(options, + renderthread::VulkanManager::ContextType::kUploadThread); + LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads"); + this->postIdleTimeoutCheck(); + } + + sk_sp<SkImage> image = + SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb); + mGrContext->submit(true); + + uploadSucceeded = (image.get() != nullptr); + }); + return uploadSucceeded; + } - std::lock_guard _lock{mLock}; + /* must be called on the upload thread after the vkLock has been acquired */ + renderthread::VulkanManager* getVulkanManager() { + if (!mVulkanManagerStrong) { + mVulkanManagerStrong = mVulkanManagerWeak.promote(); + + // create a new manager if we couldn't promote the weak ref + if (!mVulkanManagerStrong) { + mVulkanManagerStrong = renderthread::VulkanManager::getInstance(); + mGrContext.reset(); + mVulkanManagerWeak = mVulkanManagerStrong; + } + } - sk_sp<SkImage> image = - SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb); - return (image.get() != nullptr); + return mVulkanManagerStrong.get(); } sk_sp<GrDirectContext> mGrContext; - renderthread::VulkanManager mVulkanManager; + sp<renderthread::VulkanManager> mVulkanManagerStrong; + wp<renderthread::VulkanManager> mVulkanManagerWeak; std::mutex mVkLock; }; @@ -428,7 +425,6 @@ void HardwareBitmapUploader::initialize() { bool usingGL = uirenderer::Properties::getRenderPipelineType() == uirenderer::RenderPipelineType::SkiaGL; createUploader(usingGL); - sUploader->initialize(); } void HardwareBitmapUploader::terminate() { diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index fc594da19708..e817ca744c58 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -184,7 +184,9 @@ static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz, proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); } proxy->setSurface(window, enableTimeout); - ANativeWindow_release(window); + if (window) { + ANativeWindow_release(window); + } } static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz, diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index aad0cca80cdc..b51f6dcfc66f 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -77,10 +77,10 @@ void RenderProxy::setName(const char* name) { } void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { - ANativeWindow_acquire(window); + if (window) { ANativeWindow_acquire(window); } mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { mContext->setSurface(win, enableTimeout); - ANativeWindow_release(win); + if (win) { ANativeWindow_release(win); } }); } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 565fb61c8994..4dcbc4458e97 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -131,8 +131,7 @@ RenderThread::RenderThread() , mFrameCallbackTaskPending(false) , mRenderState(nullptr) , mEglManager(nullptr) - , mFunctorManager(WebViewFunctorManager::instance()) - , mVkManager(nullptr) { + , mFunctorManager(WebViewFunctorManager::instance()) { Properties::load(); start("RenderThread"); } @@ -166,7 +165,7 @@ void RenderThread::initThreadLocals() { initializeChoreographer(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); - mVkManager = new VulkanManager(); + mVkManager = VulkanManager::getInstance(); mCacheManager = new CacheManager(); } @@ -196,7 +195,8 @@ void RenderThread::requireGlContext() { } void RenderThread::requireVkContext() { - if (mVkManager->hasVkContext()) { + // the getter creates the context in the event it had been destroyed by destroyRenderingContext + if (vulkanManager().hasVkContext()) { return; } mVkManager->initialize(); @@ -222,11 +222,16 @@ void RenderThread::destroyRenderingContext() { mEglManager->destroy(); } } else { - if (vulkanManager().hasVkContext()) { - setGrContext(nullptr); - vulkanManager().destroy(); - } + setGrContext(nullptr); + mVkManager.clear(); + } +} + +VulkanManager& RenderThread::vulkanManager() { + if (!mVkManager.get()) { + mVkManager = VulkanManager::getInstance(); } + return *mVkManager.get(); } void RenderThread::dumpGraphicsMemory(int fd) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index b8ce55650516..d7dc00b8a5c1 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -110,7 +110,7 @@ public: void setGrContext(sk_sp<GrDirectContext> cxt); CacheManager& cacheManager() { return *mCacheManager; } - VulkanManager& vulkanManager() { return *mVkManager; } + VulkanManager& vulkanManager(); sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap); void dumpGraphicsMemory(int fd); @@ -188,7 +188,7 @@ private: sk_sp<GrDirectContext> mGrContext; CacheManager* mCacheManager; - VulkanManager* mVkManager; + sp<VulkanManager> mVkManager; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 249936eb485e..0c5cf682e566 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -57,12 +57,22 @@ static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& fe #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) -void VulkanManager::destroy() { - if (VK_NULL_HANDLE != mCommandPool) { - mDestroyCommandPool(mDevice, mCommandPool, nullptr); - mCommandPool = VK_NULL_HANDLE; +sp<VulkanManager> VulkanManager::getInstance() { + // cache a weakptr to the context to enable a second thread to share the same vulkan state + static wp<VulkanManager> sWeakInstance = nullptr; + static std::mutex sLock; + + std::lock_guard _lock{sLock}; + sp<VulkanManager> vulkanManager = sWeakInstance.promote(); + if (!vulkanManager.get()) { + vulkanManager = new VulkanManager(); + sWeakInstance = vulkanManager; } + return vulkanManager; +} + +VulkanManager::~VulkanManager() { if (mDevice != VK_NULL_HANDLE) { mDeviceWaitIdle(mDevice); mDestroyDevice(mDevice, nullptr); @@ -73,6 +83,7 @@ void VulkanManager::destroy() { } mGraphicsQueue = VK_NULL_HANDLE; + mAHBUploadQueue = VK_NULL_HANDLE; mPresentQueue = VK_NULL_HANDLE; mDevice = VK_NULL_HANDLE; mPhysicalDevice = VK_NULL_HANDLE; @@ -175,6 +186,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe for (uint32_t i = 0; i < queueCount; i++) { if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { mGraphicsQueueIndex = i; + LOG_ALWAYS_FATAL_IF(queueProps[i].queueCount < 2); break; } } @@ -283,7 +295,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe queueNextPtr, // pNext 0, // VkDeviceQueueCreateFlags mGraphicsQueueIndex, // queueFamilyIndex - 1, // queueCount + 2, // queueCount queuePriorities, // pQueuePriorities }, { @@ -347,6 +359,7 @@ void VulkanManager::initialize() { this->setupDevice(mExtensions, mPhysicalDeviceFeatures2); mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); + mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue); // create the command pool for the command buffers if (VK_NULL_HANDLE == mCommandPool) { @@ -369,7 +382,8 @@ void VulkanManager::initialize() { } } -sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options) { +sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options, + ContextType contextType) { auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); @@ -381,7 +395,8 @@ sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& opti backendContext.fInstance = mInstance; backendContext.fPhysicalDevice = mPhysicalDevice; backendContext.fDevice = mDevice; - backendContext.fQueue = mGraphicsQueue; + backendContext.fQueue = (contextType == ContextType::kRenderThread) ? mGraphicsQueue + : mAHBUploadQueue; backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; backendContext.fMaxAPIVersion = mAPIVersion; backendContext.fVkExtensions = &mExtensions; diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 3f2df8d75d89..13335f32ef06 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -43,10 +43,9 @@ class RenderThread; // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue, // which are re-used by CanvasContext. This class is created once and should be used by all vulkan // windowing contexts. The VulkanManager must be initialized before use. -class VulkanManager { +class VulkanManager final : public RefBase { public: - explicit VulkanManager() {} - ~VulkanManager() { destroy(); } + static sp<VulkanManager> getInstance(); // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must // be call once before use of the VulkanManager. Multiple calls after the first will simiply @@ -68,9 +67,6 @@ public: Frame dequeueNextBuffer(VulkanSurface* surface); void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect); - // Cleans up all the global state in the VulkanManger. - void destroy(); - // Inserts a wait on fence command into the Vulkan command buffer. status_t fenceWait(int fence, GrDirectContext* grContext); @@ -83,12 +79,24 @@ public: // the internal state of VulkanManager: VulkanManager must be alive to use the returned value. VkFunctorInitParams getVkFunctorInitParams() const; - sk_sp<GrDirectContext> createContext(const GrContextOptions& options); + + enum class ContextType { + kRenderThread, + kUploadThread + }; + + // returns a Skia graphic context used to draw content on the specified thread + sk_sp<GrDirectContext> createContext(const GrContextOptions& options, + ContextType contextType = ContextType::kRenderThread); uint32_t getDriverVersion() const { return mDriverVersion; } private: friend class VulkanSurface; + + explicit VulkanManager() {} + ~VulkanManager(); + // Sets up the VkInstance and VkDevice objects. Also fills out the passed in // VkPhysicalDeviceFeatures struct. void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&); @@ -154,6 +162,7 @@ private: uint32_t mGraphicsQueueIndex; VkQueue mGraphicsQueue = VK_NULL_HANDLE; + VkQueue mAHBUploadQueue = VK_NULL_HANDLE; uint32_t mPresentQueueIndex; VkQueue mPresentQueue = VK_NULL_HANDLE; VkCommandPool mCommandPool = VK_NULL_HANDLE; diff --git a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml index b8e1edc46da7..20aa5f79c5cc 100644 --- a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml +++ b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml @@ -22,7 +22,8 @@ <item target="attr/icon" value="@attr/icon"/> <item target="attr/selectedIcon" value="@attr/selectedIcon"/> - <item target="attr/intent" value="@attr/longIntent"/> + <item target="attr/intent" value="@attr/intent"/> + <item target="attr/longIntent" value="@attr/longIntent"/> <item target="attr/componentNames" value="@attr/componentNames"/> <item target="attr/highlightWhenSelected" value="@attr/highlightWhenSelected"/> <item target="attr/categories" value="@attr/categories"/> diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java index b2f98ecde513..24d9d09d2ca9 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java @@ -22,7 +22,6 @@ import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SystemServicesModule; import com.android.systemui.dagger.SystemUIModule; -import com.android.systemui.onehanded.dagger.OneHandedModule; import com.android.systemui.pip.phone.dagger.PipModule; import dagger.Subcomponent; @@ -36,7 +35,6 @@ import dagger.Subcomponent; DependencyProvider.class, DependencyBinder.class, PipModule.class, - OneHandedModule.class, SystemServicesModule.class, SystemUIModule.class, CarSystemUIModule.class, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 4ce9f5a9edc6..af008b996172 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -113,6 +113,7 @@ <uses-permission android:name="android.permission.SET_ORIENTATION" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.MONITOR_INPUT" /> + <uses-permission android:name="android.permission.INPUT_CONSUMER" /> <!-- DreamManager --> <uses-permission android:name="android.permission.READ_DREAM_STATE" /> diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index b42d71abaa6d..df66bf5a1051 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -45,4 +45,5 @@ -keep class com.android.systemui.dagger.GlobalRootComponent { *; } -keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; } --keep class com.android.systemui.dagger.Dagger** { *; }
\ No newline at end of file +-keep class com.android.systemui.dagger.Dagger** { *; } +-keep class com.android.systemui.tv.Dagger** { *; }
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/bubble_stack_user_education.xml b/packages/SystemUI/res/layout/bubble_stack_user_education.xml index 616403219bc6..fe1ed4b6f726 100644 --- a/packages/SystemUI/res/layout/bubble_stack_user_education.xml +++ b/packages/SystemUI/res/layout/bubble_stack_user_education.xml @@ -15,8 +15,8 @@ ~ limitations under the License. --> <LinearLayout + android:id="@+id/stack_education_layout" xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/user_education_view" android:layout_height="wrap_content" android:layout_width="wrap_content" android:paddingTop="48dp" @@ -25,26 +25,29 @@ android:paddingEnd="16dp" android:layout_marginEnd="24dp" android:orientation="vertical" - android:background="@drawable/bubble_stack_user_education_bg"> - + android:background="@drawable/bubble_stack_user_education_bg" + > <TextView - android:id="@+id/user_education_title" + android:id="@+id/stack_education_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="16dp" android:fontFamily="@*android:string/config_bodyFontFamilyMedium" android:maxLines="2" android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" android:text="@string/bubbles_user_education_title" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> <TextView - android:id="@+id/user_education_description" + android:id="@+id/stack_education_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" android:text="@string/bubbles_user_education_description" android:fontFamily="@*android:string/config_bodyFontFamily" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> - </LinearLayout> diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml index 213bb923db65..b51dc93dc373 100644 --- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml +++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml @@ -14,77 +14,77 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.bubbles.ManageEducationView + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" + android:id="@+id/manage_education_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" + android:paddingTop="28dp" + android:paddingBottom="16dp" + android:paddingStart="@dimen/bubble_expanded_view_padding" + android:paddingEnd="48dp" + android:layout_marginEnd="24dp" + android:orientation="vertical" + android:background="@drawable/bubble_stack_user_education_bg" > - <LinearLayout + + <TextView + android:id="@+id/user_education_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:id="@+id/manage_education_view" - android:clickable="true" - android:paddingTop="28dp" + android:paddingStart="16dp" android:paddingBottom="16dp" - android:paddingStart="@dimen/bubble_expanded_view_padding" - android:paddingEnd="48dp" - android:layout_marginEnd="24dp" - android:orientation="vertical" - android:background="@drawable/bubble_stack_user_education_bg" - > + android:fontFamily="@*android:string/config_bodyFontFamilyMedium" + android:maxLines="2" + android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" + android:text="@string/bubbles_user_education_manage_title" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> - <TextView - android:id="@+id/user_education_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingBottom="16dp" - android:fontFamily="@*android:string/config_bodyFontFamilyMedium" - android:maxLines="2" - android:ellipsize="end" - android:text="@string/bubbles_user_education_manage_title" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> + <TextView + android:id="@+id/user_education_description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingStart="16dp" + android:paddingBottom="24dp" + android:text="@string/bubbles_user_education_manage" + android:maxLines="2" + android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> - <TextView - android:id="@+id/user_education_description" + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:id="@+id/button_layout" + android:orientation="horizontal" > + + <com.android.systemui.statusbar.AlphaOptimizedButton + style="@android:style/Widget.Material.Button.Borderless" + android:id="@+id/manage" + android:layout_gravity="start" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingBottom="24dp" - android:text="@string/bubbles_user_education_manage" - android:maxLines="2" - android:ellipsize="end" - android:fontFamily="@*android:string/config_bodyFontFamily" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> + android:focusable="true" + android:clickable="false" + android:text="@string/manage_bubbles_text" + android:textColor="?attr/wallpaperTextColor" + /> - <LinearLayout - android:layout_height="wrap_content" + <com.android.systemui.statusbar.AlphaOptimizedButton + style="@android:style/Widget.Material.Button.Borderless" + android:id="@+id/got_it" + android:layout_gravity="start" android:layout_width="wrap_content" - android:id="@+id/button_layout" - android:orientation="horizontal" > - - <com.android.systemui.statusbar.AlphaOptimizedButton - style="@android:style/Widget.Material.Button.Borderless" - android:id="@+id/manage" - android:layout_gravity="start" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:focusable="true" - android:clickable="false" - android:text="@string/manage_bubbles_text" - android:textColor="?attr/wallpaperTextColor" - /> - - <com.android.systemui.statusbar.AlphaOptimizedButton - style="@android:style/Widget.Material.Button.Borderless" - android:id="@+id/got_it" - android:layout_gravity="start" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:focusable="true" - android:text="@string/bubbles_user_education_got_it" - android:textColor="?attr/wallpaperTextColor" - /> - </LinearLayout> + android:layout_height="wrap_content" + android:focusable="true" + android:text="@string/bubbles_user_education_got_it" + android:textColor="?attr/wallpaperTextColor" + /> </LinearLayout> -</com.android.systemui.bubbles.ManageEducationView> +</LinearLayout> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 765a9422585a..d12f0103238d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1342,7 +1342,7 @@ <dimen name="controls_app_divider_height">2dp</dimen> <dimen name="controls_app_divider_side_margin">32dp</dimen> - <dimen name="controls_card_margin">2dp</dimen> + <dimen name="controls_card_margin">@dimen/control_base_item_margin</dimen> <item name="control_card_elevation" type="dimen" format="float">15</item> <dimen name="controls_dialog_padding">32dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 59af458e2402..17b840cc7a20 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -162,8 +162,8 @@ public class ExpandHelper implements Gefingerpoken { * * @param context application context * @param callback the container that holds the items to be manipulated - * @param small the smallest allowable size for the manuipulated items. - * @param large the largest allowable size for the manuipulated items. + * @param small the smallest allowable size for the manipulated items. + * @param large the largest allowable size for the manipulated items. */ public ExpandHelper(Context context, Callback callback, int small, int large) { mSmallSize = small; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index ec9644af7013..64df2b99ee22 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -19,11 +19,7 @@ package com.android.systemui.bubbles; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION; -import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW; -import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -38,7 +34,6 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Outline; @@ -54,7 +49,6 @@ import android.provider.Settings; import android.util.Log; import android.view.Choreographer; import android.view.DisplayCutout; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -70,10 +64,8 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; -import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; @@ -82,7 +74,6 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Interpolators; import com.android.systemui.Prefs; import com.android.systemui.R; @@ -115,10 +106,6 @@ public class BubbleStackView extends FrameLayout implements ViewTreeObserver.OnComputeInternalInsetsListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES; - /** Animation durations for bubble stack user education views. **/ - static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200; - private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40; - /** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */ static final float FLYOUT_DRAG_PERCENT_DISMISS = 0.25f; @@ -556,7 +543,7 @@ public class BubbleStackView extends FrameLayout // Otherwise, we either tapped the stack (which means we're collapsed // and should expand) or the currently selected bubble (we're expanded // and should collapse). - if (!maybeShowStackUserEducation()) { + if (!maybeShowStackEdu()) { mBubbleData.setExpanded(!mBubbleData.isExpanded()); } } @@ -582,7 +569,9 @@ public class BubbleStackView extends FrameLayout } if (mBubbleData.isExpanded()) { - maybeShowManageEducation(false /* show */); + if (mManageEduView != null) { + mManageEduView.hide(false /* show */); + } // If we're expanded, tell the animation controller to prepare to drag this bubble, // dispatching to the individual bubble magnet listener. @@ -637,7 +626,9 @@ public class BubbleStackView extends FrameLayout mExpandedAnimationController.dragBubbleOut( v, viewInitialX + dx, viewInitialY + dy); } else { - hideStackUserEducation(false /* fromExpansion */); + if (mStackEduView != null) { + mStackEduView.hide(false /* fromExpansion */); + } mStackAnimationController.moveStackFromTouch( viewInitialX + dx, viewInitialY + dy); } @@ -684,7 +675,7 @@ public class BubbleStackView extends FrameLayout private OnClickListener mFlyoutClickListener = new OnClickListener() { @Override public void onClick(View view) { - if (maybeShowStackUserEducation()) { + if (maybeShowStackEdu()) { // If we're showing user education, don't open the bubble show the education first mBubbleToExpandAfterFlyoutCollapse = null; } else { @@ -728,7 +719,7 @@ public class BubbleStackView extends FrameLayout mFlyout.removeCallbacks(mHideFlyout); animateFlyoutCollapsed(shouldDismiss, velX); - maybeShowStackUserEducation(); + maybeShowStackEdu(); } }; @@ -737,14 +728,8 @@ public class BubbleStackView extends FrameLayout @Nullable private BubbleOverflow mBubbleOverflow; - - private boolean mShouldShowUserEducation; - private boolean mAnimatingEducationAway; - private View mUserEducationView; - - private boolean mShouldShowManageEducation; - private ManageEducationView mManageEducationView; - private boolean mAnimatingManageEducationAway; + private StackEducationView mStackEduView; + private ManageEducationView mManageEduView; private ViewGroup mManageMenu; private ImageView mManageSettingsIcon; @@ -805,8 +790,6 @@ public class BubbleStackView extends FrameLayout onBubbleAnimatedOut); mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; - setUpUserEducation(); - // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or // is centered. It greatly simplifies translation positioning/animations. Views that will // actually lay out differently in RTL, such as the flyout and expanded view, will set their @@ -819,6 +802,8 @@ public class BubbleStackView extends FrameLayout mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + updateUserEdu(); + mExpandedViewContainer = new FrameLayout(context); mExpandedViewContainer.setElevation(elevation); mExpandedViewContainer.setClipChildren(false); @@ -1092,48 +1077,66 @@ public class BubbleStackView extends FrameLayout addView(mManageMenu); } - private void setUpUserEducation() { - if (mUserEducationView != null) { - removeView(mUserEducationView); - } - mShouldShowUserEducation = shouldShowBubblesEducation(); - if (DEBUG_USER_EDUCATION) { - Log.d(TAG, "shouldShowUserEducation: " + mShouldShowUserEducation); + /** + * Whether the educational view should show for the expanded view "manage" menu. + */ + private boolean shouldShowManageEdu() { + final boolean seen = Prefs.getBoolean(mContext, + Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false /* default */); + final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext)) + && mExpandedBubble != null; + if (BubbleDebugConfig.DEBUG_USER_EDUCATION) { + Log.d(TAG, "Show manage edu: " + shouldShow); } - if (mShouldShowUserEducation) { - mUserEducationView = mInflater.inflate(R.layout.bubble_stack_user_education, this, - false /* attachToRoot */); - mUserEducationView.setVisibility(GONE); - - final TypedArray ta = mContext.obtainStyledAttributes( - new int[] {android.R.attr.colorAccent, - android.R.attr.textColorPrimaryInverse}); - final int bgColor = ta.getColor(0, Color.BLACK); - int textColor = ta.getColor(1, Color.WHITE); - ta.recycle(); - textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true); + return shouldShow; + } - TextView title = mUserEducationView.findViewById(R.id.user_education_title); - TextView description = mUserEducationView.findViewById(R.id.user_education_description); - title.setTextColor(textColor); - description.setTextColor(textColor); + private void maybeShowManageEdu() { + if (!shouldShowManageEdu()) { + return; + } + if (mManageEduView == null) { + mManageEduView = new ManageEducationView(mContext); + addView(mManageEduView); + } + mManageEduView.show(mExpandedBubble.getExpandedView(), mTempRect); + } - updateUserEducationForLayoutDirection(); - addView(mUserEducationView); + /** + * Whether education view should show for the collapsed stack. + */ + private boolean shouldShowStackEdu() { + final boolean seen = Prefs.getBoolean(getContext(), + Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION, false /* default */); + final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext); + if (BubbleDebugConfig.DEBUG_USER_EDUCATION) { + Log.d(TAG, "Show stack edu: " + shouldShow); } + return shouldShow; + } - if (mManageEducationView != null) { - removeView(mManageEducationView); + /** + * @return true if education view for collapsed stack should show and was not showing before. + */ + private boolean maybeShowStackEdu() { + if (!shouldShowStackEdu()) { + return false; + } + if (mStackEduView == null) { + mStackEduView = new StackEducationView(mContext); + addView(mStackEduView); } - mShouldShowManageEducation = shouldShowManageEducation(); - if (DEBUG_USER_EDUCATION) { - Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation); + return mStackEduView.show(mStackAnimationController.getStartPosition()); + } + + private void updateUserEdu() { + maybeShowStackEdu(); + if (mManageEduView != null) { + mManageEduView.invalidate(); } - if (mShouldShowManageEducation) { - mManageEducationView = (ManageEducationView) - mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */, - false /* attachToRoot */); - addView(mManageEducationView); + maybeShowManageEdu(); + if (mStackEduView != null) { + mStackEduView.invalidate(); } } @@ -1164,9 +1167,9 @@ public class BubbleStackView extends FrameLayout */ public void onThemeChanged() { setUpFlyout(); - setUpUserEducation(); setUpManageMenu(); updateOverflow(); + updateUserEdu(); updateExpandedViewTheme(); } @@ -1197,12 +1200,11 @@ public class BubbleStackView extends FrameLayout public void onLayoutDirectionChanged(int direction) { mManageMenu.setLayoutDirection(direction); mFlyout.setLayoutDirection(direction); - if (mUserEducationView != null) { - mUserEducationView.setLayoutDirection(direction); - updateUserEducationForLayoutDirection(); + if (mStackEduView != null) { + mStackEduView.setLayoutDirection(direction); } - if (mManageEducationView != null) { - mManageEducationView.setLayoutDirection(direction); + if (mManageEduView != null) { + mManageEduView.setLayoutDirection(direction); } updateExpandedViewDirection(direction); } @@ -1446,7 +1448,7 @@ public class BubbleStackView extends FrameLayout Log.d(TAG, "addBubble: " + bubble); } - if (getBubbleCount() == 0 && mShouldShowUserEducation) { + if (getBubbleCount() == 0 && shouldShowStackEdu()) { // Override the default stack position if we're showing user education. mStackAnimationController.setStackPosition( mStackAnimationController.getStartPosition()); @@ -1649,115 +1651,6 @@ public class BubbleStackView extends FrameLayout notifyExpansionChanged(mExpandedBubble, mIsExpanded); } - /** - * If necessary, shows the user education view for the bubble stack. This appears the first - * time a user taps on a bubble. - * - * @return true if user education was shown, false otherwise. - */ - private boolean maybeShowStackUserEducation() { - if (mShouldShowUserEducation && mUserEducationView.getVisibility() != VISIBLE) { - mUserEducationView.setAlpha(0); - mUserEducationView.setVisibility(VISIBLE); - updateUserEducationForLayoutDirection(); - - // Post so we have height of mUserEducationView - mUserEducationView.post(() -> { - final int viewHeight = mUserEducationView.getHeight(); - PointF stackPosition = mStackAnimationController.getStartPosition(); - final float translationY = stackPosition.y + (mBubbleSize / 2) - (viewHeight / 2); - mUserEducationView.setTranslationY(translationY); - mUserEducationView.animate() - .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION) - .setInterpolator(FAST_OUT_SLOW_IN) - .alpha(1); - }); - Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, true); - return true; - } - return false; - } - - private void updateUserEducationForLayoutDirection() { - if (mUserEducationView == null) { - return; - } - LinearLayout textLayout = mUserEducationView.findViewById(R.id.user_education_view); - TextView title = mUserEducationView.findViewById(R.id.user_education_title); - TextView description = mUserEducationView.findViewById(R.id.user_education_description); - boolean isLtr = - getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR; - if (isLtr) { - mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_LTR); - textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg); - title.setGravity(Gravity.LEFT); - description.setGravity(Gravity.LEFT); - } else { - mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_RTL); - textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl); - title.setGravity(Gravity.RIGHT); - description.setGravity(Gravity.RIGHT); - } - } - - /** - * If necessary, hides the user education view for the bubble stack. - * - * @param fromExpansion if true this indicates the hide is happening due to the bubble being - * expanded, false if due to a touch outside of the bubble stack. - */ - void hideStackUserEducation(boolean fromExpansion) { - if (mShouldShowUserEducation - && mUserEducationView.getVisibility() == VISIBLE - && !mAnimatingEducationAway) { - mAnimatingEducationAway = true; - mUserEducationView.animate() - .alpha(0) - .setDuration(fromExpansion - ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT - : ANIMATE_STACK_USER_EDUCATION_DURATION) - .withEndAction(() -> { - mAnimatingEducationAway = false; - mShouldShowUserEducation = shouldShowBubblesEducation(); - mUserEducationView.setVisibility(GONE); - }); - } - } - - /** - * If necessary, toggles the user education view for the manage button. This is shown when the - * bubble stack is expanded for the first time. - * - * @param show whether the user education view should show or not. - */ - void maybeShowManageEducation(boolean show) { - if (mManageEducationView == null) { - return; - } - if (show - && mShouldShowManageEducation - && mManageEducationView.getVisibility() != VISIBLE - && mIsExpanded - && mExpandedBubble.getExpandedView() != null) { - mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect, - () -> maybeShowManageEducation(false) /* run on click */); - Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true); - } else if (!show - && mManageEducationView.getVisibility() == VISIBLE - && !mAnimatingManageEducationAway) { - mManageEducationView.animate() - .alpha(0) - .setDuration(mIsExpansionAnimating - ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT - : ANIMATE_STACK_USER_EDUCATION_DURATION) - .withEndAction(() -> { - mAnimatingManageEducationAway = false; - mShouldShowManageEducation = shouldShowManageEducation(); - mManageEducationView.setVisibility(GONE); - }); - } - } - void showExpandedViewContents(int displayId) { if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null @@ -1791,7 +1684,9 @@ public class BubbleStackView extends FrameLayout cancelDelayedExpandCollapseSwitchAnimations(); mIsExpanded = true; - hideStackUserEducation(true /* fromExpansion */); + if (mStackEduView != null) { + mStackEduView.hide(true /* fromExpansion */); + } beforeExpandedViewAnimation(); mBubbleContainer.setActiveController(mExpandedAnimationController); @@ -1799,7 +1694,9 @@ public class BubbleStackView extends FrameLayout updatePointerPosition(); mExpandedAnimationController.expandFromStack(() -> { afterExpandedViewAnimation(); - maybeShowManageEducation(true); + if (mIsExpanded && mExpandedBubble.getExpandedView() != null) { + maybeShowManageEdu(); + } } /* after */); mExpandedViewContainer.setTranslationX(0); @@ -1936,7 +1833,9 @@ public class BubbleStackView extends FrameLayout .withEndActions(() -> { final BubbleViewProvider previouslySelected = mExpandedBubble; beforeExpandedViewAnimation(); - maybeShowManageEducation(false); + if (mManageEduView != null) { + mManageEduView.hide(false /* fromExpansion */); + } if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "animateCollapse"); @@ -2104,8 +2003,8 @@ public class BubbleStackView extends FrameLayout // from any location. if (!mIsExpanded || mShowingManage - || (mManageEducationView != null - && mManageEducationView.getVisibility() == VISIBLE)) { + || (mManageEduView != null + && mManageEduView.getVisibility() == VISIBLE)) { touchableRegion.setEmpty(); } } @@ -2289,7 +2188,7 @@ public class BubbleStackView extends FrameLayout if (flyoutMessage == null || flyoutMessage.message == null || !bubble.showFlyout() - || (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) + || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) || isExpanded() || mIsExpansionAnimating || mIsGestureInProgress @@ -2398,7 +2297,7 @@ public class BubbleStackView extends FrameLayout * them. */ public void getTouchableRegion(Rect outRect) { - if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) { + if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) { // When user education shows then capture all touches outRect.set(0, 0, getWidth(), getHeight()); return; @@ -2770,18 +2669,6 @@ public class BubbleStackView extends FrameLayout return mExpandedBubble.getExpandedView().performBackPressIfNeeded(); } - /** Whether the educational view should appear for bubbles. **/ - private boolean shouldShowBubblesEducation() { - return BubbleDebugConfig.forceShowUserEducation(getContext()) - || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, false); - } - - /** Whether the educational view should appear for the expanded view "manage" button. **/ - private boolean shouldShowManageEducation() { - return BubbleDebugConfig.forceShowUserEducation(getContext()) - || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false); - } - /** For debugging only */ List<Bubble> getBubblesOnScreen() { List<Bubble> bubbles = new ArrayList<>(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt index c58ab31c4561..26a9773f9bb8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt @@ -18,52 +18,56 @@ package com.android.systemui.bubbles import android.content.Context import android.graphics.Color import android.graphics.Rect -import android.util.AttributeSet -import android.view.Gravity +import android.view.LayoutInflater import android.view.View import android.widget.Button import android.widget.LinearLayout import android.widget.TextView import com.android.internal.util.ContrastColorUtil import com.android.systemui.Interpolators +import com.android.systemui.Prefs +import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION import com.android.systemui.R /** - * Educational view to highlight the manage button that allows a user to configure the settings + * User education view to highlight the manage button that allows a user to configure the settings * for the bubble. Shown only the first time a user expands a bubble. */ -class ManageEducationView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, - defStyleRes: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { +class ManageEducationView constructor(context: Context) : LinearLayout(context) { + + private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView" + else BubbleDebugConfig.TAG_BUBBLES + + private val ANIMATE_DURATION : Long = 200 + private val ANIMATE_DURATION_SHORT : Long = 40 private val manageView by lazy { findViewById<View>(R.id.manage_education_view) } private val manageButton by lazy { findViewById<Button>(R.id.manage) } private val gotItButton by lazy { findViewById<Button>(R.id.got_it) } private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) } private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) } - private var isInflated = false + + private var isHiding = false init { - this.visibility = View.GONE - this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() - this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE + LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this); + visibility = View.GONE + elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() + + // BubbleStackView forces LTR by default + // since most of Bubble UI direction depends on positioning by the user. + // This view actually lays out differently in RTL, so we set layout LOCALE here. + layoutDirection = View.LAYOUT_DIRECTION_LOCALE } - override fun setLayoutDirection(direction: Int) { - super.setLayoutDirection(direction) - // setLayoutDirection runs before onFinishInflate - // so skip if views haven't inflated; otherwise we'll get NPEs - if (!isInflated) return - setDirection() + override fun setLayoutDirection(layoutDirection: Int) { + super.setLayoutDirection(layoutDirection) + setDrawableDirection() } override fun onFinishInflate() { super.onFinishInflate() - isInflated = true - setDirection() + layoutDirection = resources.configuration.layoutDirection setTextColor() } @@ -78,29 +82,35 @@ class ManageEducationView @JvmOverloads constructor( descTextView.setTextColor(textColor) } - fun setDirection() { + private fun setDrawableDirection() { manageView.setBackgroundResource( if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL) R.drawable.bubble_stack_user_education_bg_rtl else R.drawable.bubble_stack_user_education_bg) - titleTextView.gravity = Gravity.START - descTextView.gravity = Gravity.START } - fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) { + /** + * If necessary, toggles the user education view for the manage button. This is shown when the + * bubble stack is expanded for the first time. + * + * @param show whether the user education view should show or not. + */ + fun show(expandedView: BubbleExpandedView, rect : Rect) { + if (visibility == VISIBLE) return + alpha = 0f visibility = View.VISIBLE post { expandedView.getManageButtonBoundsOnScreen(rect) - with(hideMenu) { - manageButton - .setOnClickListener { - expandedView.findViewById<View>(R.id.settings_button).performClick() - this.run() - } - gotItButton.setOnClickListener { this.run() } - setOnClickListener { this.run() } - } + + manageButton + .setOnClickListener { + expandedView.findViewById<View>(R.id.settings_button).performClick() + hide(true /* isStackExpanding */) + } + gotItButton.setOnClickListener { hide(true /* isStackExpanding */) } + setOnClickListener { hide(true /* isStackExpanding */) } + with(manageView) { translationX = 0f val inset = resources.getDimensionPixelSize( @@ -109,9 +119,27 @@ class ManageEducationView @JvmOverloads constructor( } bringToFront() animate() - .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong()) + .setDuration(ANIMATE_DURATION) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .alpha(1f) } + setShouldShow(false) + } + + fun hide(isStackExpanding: Boolean) { + if (visibility != VISIBLE || isHiding) return + + animate() + .withStartAction { isHiding = true } + .alpha(0f) + .setDuration(if (isStackExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION) + .withEndAction { + isHiding = false + visibility = GONE + }; + } + + private fun setShouldShow(shouldShow: Boolean) { + Prefs.putBoolean(context, HAS_SEEN_BUBBLES_MANAGE_EDUCATION, !shouldShow) } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt new file mode 100644 index 000000000000..3e4c729d8315 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt @@ -0,0 +1,134 @@ +/* + * 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.bubbles + +import android.content.Context +import android.graphics.Color +import android.graphics.PointF +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.android.internal.util.ContrastColorUtil +import com.android.systemui.Interpolators +import com.android.systemui.Prefs +import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION +import com.android.systemui.R + +/** + * User education view to highlight the collapsed stack of bubbles. + * Shown only the first time a user taps the stack. + */ +class StackEducationView constructor(context: Context) : LinearLayout(context){ + + private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView" + else BubbleDebugConfig.TAG_BUBBLES + + private val ANIMATE_DURATION : Long = 200 + private val ANIMATE_DURATION_SHORT : Long = 40 + + private val view by lazy { findViewById<View>(R.id.stack_education_layout) } + private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) } + private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) } + + private var isHiding = false + + init { + LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this); + + visibility = View.GONE + elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() + + // BubbleStackView forces LTR by default + // since most of Bubble UI direction depends on positioning by the user. + // This view actually lays out differently in RTL, so we set layout LOCALE here. + layoutDirection = View.LAYOUT_DIRECTION_LOCALE + } + + override fun setLayoutDirection(layoutDirection: Int) { + super.setLayoutDirection(layoutDirection) + setDrawableDirection() + } + + override fun onFinishInflate() { + super.onFinishInflate() + layoutDirection = resources.configuration.layoutDirection + setTextColor() + } + + private fun setTextColor() { + val ta = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent, + android.R.attr.textColorPrimaryInverse)) + val bgColor = ta.getColor(0 /* index */, Color.BLACK) + var textColor = ta.getColor(1 /* index */, Color.WHITE) + ta.recycle() + textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true) + titleTextView.setTextColor(textColor) + descTextView.setTextColor(textColor) + } + + private fun setDrawableDirection() { + view.setBackgroundResource( + if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) + R.drawable.bubble_stack_user_education_bg + else R.drawable.bubble_stack_user_education_bg_rtl) + } + + /** + * If necessary, shows the user education view for the bubble stack. This appears the first + * time a user taps on a bubble. + * + * @return true if user education was shown, false otherwise. + */ + fun show(stackPosition: PointF) : Boolean{ + if (visibility == VISIBLE) return false + + setAlpha(0f) + setVisibility(View.VISIBLE) + post { + with(view) { + val bubbleSize = context.resources.getDimensionPixelSize( + R.dimen.individual_bubble_size) + translationY = stackPosition.y + bubbleSize / 2 - getHeight() / 2 + } + animate() + .setDuration(ANIMATE_DURATION) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .alpha(1f) + } + setShouldShow(false) + return true + } + + /** + * If necessary, hides the stack education view. + * + * @param fromExpansion if true this indicates the hide is happening due to the bubble being + * expanded, false if due to a touch outside of the bubble stack. + */ + fun hide(fromExpansion: Boolean) { + if (visibility != VISIBLE || isHiding) return + + animate() + .alpha(0f) + .setDuration(if (fromExpansion) ANIMATE_DURATION_SHORT else ANIMATE_DURATION) + .withEndAction { visibility = GONE } + } + + private fun setShouldShow(shouldShow: Boolean) { + Prefs.putBoolean(context, HAS_SEEN_BUBBLES_EDUCATION, !shouldShow) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index c683a87d6282..31830b94e8e4 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -72,8 +72,13 @@ class ControlAdapter( TYPE_CONTROL -> { ControlHolder( layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { - layoutParams.apply { + (layoutParams as ViewGroup.MarginLayoutParams).apply { width = ViewGroup.LayoutParams.MATCH_PARENT + // Reset margins as they will be set through the decoration + topMargin = 0 + bottomMargin = 0 + leftMargin = 0 + rightMargin = 0 } elevation = this@ControlAdapter.elevation background = parent.context.getDrawable( @@ -386,7 +391,7 @@ class MarginItemDecorator( val type = parent.adapter?.getItemViewType(position) if (type == ControlAdapter.TYPE_CONTROL) { outRect.apply { - top = topMargin + top = topMargin * 2 // Use double margin, as we are not setting bottom left = sideMargins right = sideMargins bottom = 0 diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 7281faf1a2c4..b606201cc803 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -22,7 +22,6 @@ import com.android.systemui.InitController; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; -import com.android.systemui.onehanded.dagger.OneHandedModule; import com.android.systemui.pip.phone.dagger.PipModule; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; @@ -37,7 +36,6 @@ import dagger.Subcomponent; DefaultComponentBinder.class, DependencyProvider.class, DependencyBinder.class, - OneHandedModule.class, PipModule.class, SystemServicesModule.class, SystemUIBinder.class, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 1a1cc072c6bf..19b0ea1db04e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -33,9 +33,6 @@ import java.io.PrintWriter; import javax.inject.Inject; -import dagger.Reusable; - -@Reusable // Don't create multiple DozeServices. public class DozeService extends DreamService implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> { private static final String TAG = "DozeService"; @@ -60,7 +57,7 @@ public class DozeService extends DreamService setWindowless(true); mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */); - DozeComponent dozeComponent = mDozeComponentBuilder.build(); + DozeComponent dozeComponent = mDozeComponentBuilder.build(this); mDozeMachine = dozeComponent.getDozeMachine(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java index 247285434df9..05050f905e60 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.doze.dagger; import com.android.systemui.doze.DozeMachine; import com.android.systemui.doze.DozeService; +import dagger.BindsInstance; import dagger.Subcomponent; /** @@ -30,7 +31,7 @@ public interface DozeComponent { /** Simple Builder for {@link DozeComponent}. */ @Subcomponent.Factory interface Builder { - DozeComponent build(); + DozeComponent build(@BindsInstance DozeMachine.Service dozeMachineService); } /** Supply a {@link DozeMachine}. */ 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 a12e280fcca6..04f7c368fdc4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java @@ -33,7 +33,6 @@ import com.android.systemui.doze.DozeScreenBrightness; import com.android.systemui.doze.DozeScreenState; import com.android.systemui.doze.DozeScreenStatePreventingAdapter; import com.android.systemui.doze.DozeSensors; -import com.android.systemui.doze.DozeService; import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter; import com.android.systemui.doze.DozeTriggers; import com.android.systemui.doze.DozeUi; @@ -52,9 +51,9 @@ public abstract class DozeModule { @Provides @DozeScope @WrappedService - static DozeMachine.Service providesWrappedService(DozeService dozeService, DozeHost dozeHost, - DozeParameters dozeParameters) { - DozeMachine.Service wrappedService = dozeService; + static DozeMachine.Service providesWrappedService(DozeMachine.Service dozeMachineService, + DozeHost dozeHost, DozeParameters dozeParameters) { + DozeMachine.Service wrappedService = dozeMachineService; wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost); wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded( wrappedService, dozeParameters); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java index 90e7e12b2b47..bb59449d114d 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedController.java @@ -49,7 +49,7 @@ import javax.inject.Inject; * Manages and manipulates the one handed states, transitions, and gesture for phones. */ @SysUISingleton -public class OneHandedManagerImpl implements OneHandedManager, Dumpable { +public class OneHandedController implements Dumpable { private static final String TAG = "OneHandedManager"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = "persist.debug.one_handed_offset_percentage"; @@ -106,7 +106,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { * Constructor of OneHandedManager */ @Inject - public OneHandedManagerImpl(Context context, + public OneHandedController(Context context, CommandQueue commandQueue, DisplayController displayController, NavigationModeController navigationModeController, @@ -137,7 +137,7 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { */ // TODO(b/161980408): Should remove extra constructor. @VisibleForTesting - OneHandedManagerImpl(Context context, + OneHandedController(Context context, CommandQueue commandQueue, DisplayController displayController, OneHandedDisplayAreaOrganizer displayAreaOrganizer, @@ -194,7 +194,6 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { /** * Enters one handed mode. */ - @Override public void startOneHanded() { if (!mDisplayAreaOrganizer.isInOneHanded()) { final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction); @@ -206,7 +205,6 @@ public class OneHandedManagerImpl implements OneHandedManager, Dumpable { /** * Exits one handed mode. */ - @Override public void stopOneHanded() { if (mDisplayAreaOrganizer.isInOneHanded()) { mDisplayAreaOrganizer.scheduleOffset(0, 0); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java index 1420811b9b30..f3be699ab821 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java @@ -103,7 +103,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, } /** - * Notified by {@link OneHandedManager}, when user update settings of Enabled or Disabled + * Notified by {@link OneHandedController}, when user update settings of Enabled or Disabled * * @param isEnabled is one handed settings enabled or not */ @@ -264,7 +264,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, } /** - * The touch(gesture) events to notify {@link OneHandedManager} start or stop one handed + * The touch(gesture) events to notify {@link OneHandedController} start or stop one handed */ public interface OneHandedGestureEventCallback { /** diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManager.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManager.java deleted file mode 100644 index 90187a298cf2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManager.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.onehanded; - -/** - * The base class of OneHandedManager - */ -public interface OneHandedManager { - - /** - * Set one handed enabled or disabled - */ - default void setOneHandedEnabled(boolean enabled) {} - - /** - * Set task stack changed to exit - */ - default void setTaskChangeToExit(boolean enabled) {} - - /** - * Exit one handed mode - */ - default void stopOneHanded() {} - - /** - * Trigger one handed mode - */ - default void startOneHanded() {} - -} diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java index 0a7eb1bdada0..8265da6a5f14 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java @@ -63,7 +63,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa } /** - * Notified by {@link OneHandedManagerImpl}, when user update settings of Enabled or Disabled + * Notified by {@link OneHandedController}, when user update settings of Enabled or Disabled * * @param isEnabled is one handed settings enabled or not */ @@ -166,7 +166,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback, Dumpa } /** - * The touch(gesture) events to notify {@link OneHandedManager} start or stop one handed + * The touch(gesture) events to notify {@link OneHandedController} start or stop one handed */ public interface OneHandedTouchEventCallback { /** diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java index cebcd4ceabe9..3348a06d5cac 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java +++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java @@ -59,7 +59,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum "com.android.internal.systemui.onehanded.gestural"; private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; - private final OneHandedManagerImpl mOneHandedManager; + private final OneHandedController mOneHandedController; private final CommandQueue mCommandQueue; private final Handler mMainHandler = new Handler(Looper.getMainLooper()); private final IOverlayManager mOverlayManager; @@ -74,8 +74,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum OneHandedEvents.writeEvent(enabled ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF); - if (mOneHandedManager != null) { - mOneHandedManager.setOneHandedEnabled(enabled); + if (mOneHandedController != null) { + mOneHandedController.setOneHandedEnabled(enabled); } // Also checks swipe to notification settings since they all need gesture overlay. @@ -125,8 +125,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum ? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON : OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF); - if (mOneHandedManager != null) { - mOneHandedManager.setTaskChangeToExit(enabled); + if (mOneHandedController != null) { + mOneHandedController.setTaskChangeToExit(enabled); } } }; @@ -138,8 +138,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum final boolean enabled = OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver()); - if (mOneHandedManager != null) { - mOneHandedManager.setSwipeToNotificationEnabled(enabled); + if (mOneHandedController != null) { + mOneHandedController.setSwipeToNotificationEnabled(enabled); } // Also checks one handed mode settings since they all need gesture overlay. @@ -152,14 +152,14 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum @Inject public OneHandedUI(Context context, CommandQueue commandQueue, - OneHandedManagerImpl oneHandedManager, + OneHandedController oneHandedController, ScreenLifecycle screenLifecycle) { super(context); if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) { Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off"); mCommandQueue = null; - mOneHandedManager = null; + mOneHandedController = null; mOverlayManager = null; mTimeoutHandler = null; mScreenLifecycle = null; @@ -167,7 +167,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum } mCommandQueue = commandQueue; - mOneHandedManager = oneHandedManager; + mOneHandedController = oneHandedController; mTimeoutHandler = OneHandedTimeoutHandler.get(); mScreenLifecycle = screenLifecycle; mOverlayManager = IOverlayManager.Stub.asInterface( @@ -260,13 +260,13 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum } private void updateSettings() { - mOneHandedManager.setOneHandedEnabled(OneHandedSettingsUtil + mOneHandedController.setOneHandedEnabled(OneHandedSettingsUtil .getSettingsOneHandedModeEnabled(mContext.getContentResolver())); mTimeoutHandler.setTimeout(OneHandedSettingsUtil .getSettingsOneHandedModeTimeout(mContext.getContentResolver())); - mOneHandedManager.setTaskChangeToExit(OneHandedSettingsUtil + mOneHandedController.setTaskChangeToExit(OneHandedSettingsUtil .getSettingsTapsAppToExit(mContext.getContentResolver())); - mOneHandedManager.setSwipeToNotificationEnabled(OneHandedSettingsUtil + mOneHandedController.setSwipeToNotificationEnabled(OneHandedSettingsUtil .getSettingsSwipeToNotificationEnabled(mContext.getContentResolver())); } @@ -295,7 +295,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum * Trigger one handed more */ public void startOneHanded() { - mOneHandedManager.startOneHanded(); + mOneHandedController.startOneHanded(); OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN); } @@ -303,7 +303,7 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum * Dismiss one handed more */ public void stopOneHanded() { - mOneHandedManager.stopOneHanded(); + mOneHandedController.stopOneHanded(); OneHandedEvents.writeEvent(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT); } @@ -314,8 +314,8 @@ public class OneHandedUI extends SystemUI implements CommandQueue.Callbacks, Dum final String innerPrefix = " "; pw.println(TAG + "one handed states: "); - if (mOneHandedManager != null) { - ((OneHandedManagerImpl) mOneHandedManager).dump(fd, pw, args); + if (mOneHandedController != null) { + mOneHandedController.dump(fd, pw, args); } if (mTimeoutHandler != null) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java index 4931388fe362..fd8ca8044acf 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java @@ -31,8 +31,6 @@ import com.android.systemui.Interpolators; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import javax.inject.Inject; - /** * Controller class of PiP animations (both from and to PiP mode). */ @@ -88,7 +86,6 @@ public class PipAnimationController { return handler; }); - @Inject PipAnimationController(PipSurfaceTransactionHelper helper) { mSurfaceTransactionHelper = helper; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java index d6aa61b0c767..b464e8adea59 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java @@ -40,13 +40,10 @@ import android.view.Gravity; import android.window.WindowContainerTransaction; import com.android.systemui.dagger.SysUISingleton; -import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import java.io.PrintWriter; -import javax.inject.Inject; - /** * Handles bounds calculation for PIP on Phone and other form factors, it keeps tracking variant * state changes originated from Window Manager and is the source of truth for PiP window bounds. @@ -57,10 +54,8 @@ public class PipBoundsHandler { private static final String TAG = PipBoundsHandler.class.getSimpleName(); private static final float INVALID_SNAP_FRACTION = -1f; - private final Context mContext; private final PipSnapAlgorithm mSnapAlgorithm; private final DisplayInfo mDisplayInfo = new DisplayInfo(); - private final DisplayController mDisplayController; private DisplayLayout mDisplayLayout; private ComponentName mLastPipComponentName; @@ -82,25 +77,10 @@ public class PipBoundsHandler { private boolean mIsShelfShowing; private int mShelfHeight; - private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener = - new DisplayController.OnDisplaysChangedListener() { - @Override - public void onDisplayAdded(int displayId) { - if (displayId == mContext.getDisplayId()) { - mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId)); - } - } - }; - - @Inject - public PipBoundsHandler(Context context, PipSnapAlgorithm pipSnapAlgorithm, - DisplayController displayController) { - mContext = context; - mSnapAlgorithm = pipSnapAlgorithm; + public PipBoundsHandler(Context context) { + mSnapAlgorithm = new PipSnapAlgorithm(context); mDisplayLayout = new DisplayLayout(); - mDisplayController = displayController; - mDisplayController.addDisplayWindowListener(mDisplaysChangedListener); - reloadResources(); + reloadResources(context); // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload // resources as it would clobber mAspectRatio when entering PiP from fullscreen which // triggers a configuration change and the resources to be reloaded. @@ -110,8 +90,8 @@ public class PipBoundsHandler { /** * TODO: move the resources to SysUI package. */ - private void reloadResources() { - final Resources res = mContext.getResources(); + private void reloadResources(Context context) { + final Resources res = context.getResources(); mDefaultAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); mDefaultStackGravity = res.getInteger( @@ -133,6 +113,19 @@ public class PipBoundsHandler { com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); } + /** + * Sets or update latest {@link DisplayLayout} when new display added or rotation callbacks + * from {@link DisplayController.OnDisplaysChangedListener} + * @param newDisplayLayout latest {@link DisplayLayout} + */ + public void setDisplayLayout(DisplayLayout newDisplayLayout) { + mDisplayLayout.set(newDisplayLayout); + } + + /** + * Update the Min edge size for {@link PipSnapAlgorithm} to calculate corresponding bounds + * @param minEdgeSize + */ public void setMinEdgeSize(int minEdgeSize) { mCurrentMinSize = minEdgeSize; } @@ -217,6 +210,14 @@ public class PipBoundsHandler { return mReentrySnapFraction != INVALID_SNAP_FRACTION; } + /** + * The {@link PipSnapAlgorithm} is couple on display bounds + * @return {@link PipSnapAlgorithm}. + */ + public PipSnapAlgorithm getSnapAlgorithm() { + return mSnapAlgorithm; + } + public Rect getDisplayBounds() { return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); } @@ -237,8 +238,8 @@ public class PipBoundsHandler { /** * Responds to IPinnedStackListener on configuration change. */ - public void onConfigurationChanged() { - reloadResources(); + public void onConfigurationChanged(Context context) { + reloadResources(context); } /** @@ -300,10 +301,10 @@ public class PipBoundsHandler { * aren't in PIP because the rotation layout is used to calculate the proper insets for the * next enter animation into PIP. */ - public void onDisplayRotationChangedNotInPip(int toRotation) { + public void onDisplayRotationChangedNotInPip(Context context, int toRotation) { // Update the display layout, note that we have to do this on every rotation even if we // aren't in PIP since we need to update the display layout to get the right resources - mDisplayLayout.rotateTo(mContext.getResources(), toRotation); + mDisplayLayout.rotateTo(context.getResources(), toRotation); // Populate the new {@link #mDisplayInfo}. // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation, @@ -319,7 +320,8 @@ public class PipBoundsHandler { * * @return {@code true} if internal {@link DisplayInfo} is rotated, {@code false} otherwise. */ - public boolean onDisplayRotationChanged(Rect outBounds, Rect oldBounds, Rect outInsetBounds, + public boolean onDisplayRotationChanged(Context context, Rect outBounds, Rect oldBounds, + Rect outInsetBounds, int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) { // Bail early if the event is not sent to current {@link #mDisplayInfo} if ((displayId != mDisplayInfo.displayId) || (fromRotation == toRotation)) { @@ -342,7 +344,7 @@ public class PipBoundsHandler { final float snapFraction = getSnapFraction(postChangeStackBounds); // Update the display layout - mDisplayLayout.rotateTo(mContext.getResources(), toRotation); + mDisplayLayout.rotateTo(context.getResources(), toRotation); // Populate the new {@link #mDisplayInfo}. // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation, @@ -546,5 +548,6 @@ public class PipBoundsHandler { pw.println(innerPrefix + "mImeHeight=" + mImeHeight); pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight); + pw.println(innerPrefix + "mSnapAlgorithm" + mSnapAlgorithm); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java index a9b32d917d85..5d23e4207c33 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipSnapAlgorithm.java @@ -22,24 +22,18 @@ import android.graphics.PointF; import android.graphics.Rect; import android.util.Size; -import javax.inject.Inject; - /** * Calculates the snap targets and the snap position for the PIP given a position and a velocity. * All bounds are relative to the display top/left. */ public class PipSnapAlgorithm { - private final Context mContext; - private final float mDefaultSizePercent; private final float mMinAspectRatioForMinSize; private final float mMaxAspectRatioForMinSize; - @Inject public PipSnapAlgorithm(Context context) { Resources res = context.getResources(); - mContext = context; mDefaultSizePercent = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); mMaxAspectRatioForMinSize = res.getFloat( diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java index e88451ca00b5..3e98169c5b2b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java @@ -27,8 +27,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.wm.shell.R; -import javax.inject.Inject; - /** * Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition. */ @@ -46,7 +44,6 @@ public class PipSurfaceTransactionHelper implements ConfigurationController.Conf private final RectF mTmpDestinationRectF = new RectF(); private final Rect mTmpDestinationRect = new Rect(); - @Inject public PipSurfaceTransactionHelper(Context context, ConfigurationController configController) { final Resources res = context.getResources(); mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 0e60c83b8392..cfc544709725 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -70,8 +70,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.Consumer; -import javax.inject.Inject; - /** * Manages PiP tasks such as resize and offset. * @@ -205,12 +203,10 @@ public class PipTaskOrganizer extends TaskOrganizer implements */ private boolean mShouldDeferEnteringPip; - @Inject public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @Nullable Divider divider, @NonNull DisplayController displayController, - @NonNull PipAnimationController pipAnimationController, @NonNull PipUiEventLogger pipUiEventLogger) { mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); @@ -218,7 +214,7 @@ public class PipTaskOrganizer extends TaskOrganizer implements mEnterExitAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); mSurfaceTransactionHelper = surfaceTransactionHelper; - mPipAnimationController = pipAnimationController; + mPipAnimationController = new PipAnimationController(mSurfaceTransactionHelper); mPipUiEventLoggerLogger = pipUiEventLogger; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitDivider = divider; diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java index 7ce2028b5f1b..8bcaa8ab5404 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipUiEventLogger.java @@ -22,9 +22,6 @@ import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.systemui.dagger.SysUISingleton; -import javax.inject.Inject; - - /** * Helper class that ends PiP log to UiEvent, see also go/uievent */ @@ -35,7 +32,6 @@ public class PipUiEventLogger { private TaskInfo mTaskInfo; - @Inject public PipUiEventLogger(UiEventLogger uiEventLogger) { mUiEventLogger = uiEventLogger; } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index facb3966f78c..ac076415cd1a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -46,7 +46,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; -import com.android.systemui.pip.PipSnapAlgorithm; +import com.android.systemui.pip.PipSurfaceTransactionHelper; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.pip.phone.dagger.PipMenuActivityClass; @@ -56,6 +56,7 @@ import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; +import com.android.systemui.stackdivider.Divider; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.FloatingContentCoordinator; @@ -82,15 +83,17 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private final Rect mTmpNormalBounds = new Rect(); protected final Rect mReentryBounds = new Rect(); - private PipBoundsHandler mPipBoundsHandler; + private DisplayController mDisplayController; private InputConsumerController mInputConsumerController; + private PipAppOpsListener mAppOpsListener; private PipMediaController mMediaController; private PipTouchHandler mTouchHandler; private PipTaskOrganizer mPipTaskOrganizer; - private PipAppOpsListener mAppOpsListener; + private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper; private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener; private boolean mIsInFixedRotation; + protected PipBoundsHandler mPipBoundsHandler; protected PipMenuActivityController mMenuController; /** @@ -101,15 +104,16 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio if (!mPipTaskOrganizer.isInPip() || mPipTaskOrganizer.isDeferringEnterPipAnimation()) { // Skip if we aren't in PIP or haven't actually entered PIP yet. We still need to update // the display layout in the bounds handler in this case. - mPipBoundsHandler.onDisplayRotationChangedNotInPip(toRotation); + mPipBoundsHandler.onDisplayRotationChangedNotInPip(mContext, toRotation); return; } // If there is an animation running (ie. from a shelf offset), then ensure that we calculate // the bounds for the next orientation using the destination bounds of the animation // TODO: Techincally this should account for movement animation bounds as well Rect currentBounds = mPipTaskOrganizer.getCurrentOrAnimatingBounds(); - final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mTmpNormalBounds, - currentBounds, mTmpInsetBounds, displayId, fromRotation, toRotation, t); + final boolean changed = mPipBoundsHandler.onDisplayRotationChanged(mContext, + mTmpNormalBounds, currentBounds, mTmpInsetBounds, displayId, fromRotation, + toRotation, t); if (changed) { // If the pip was in the offset zone earlier, adjust the new bounds to the bottom of the // movement bounds @@ -135,16 +139,22 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private DisplayController.OnDisplaysChangedListener mFixedRotationListener = new DisplayController.OnDisplaysChangedListener() { - @Override - public void onFixedRotationStarted(int displayId, int newRotation) { - mIsInFixedRotation = true; - } - - @Override - public void onFixedRotationFinished(int displayId) { - mIsInFixedRotation = false; - } - }; + @Override + public void onFixedRotationStarted(int displayId, int newRotation) { + mIsInFixedRotation = true; + } + + @Override + public void onFixedRotationFinished(int displayId) { + mIsInFixedRotation = false; + } + + @Override + public void onDisplayAdded(int displayId) { + mPipBoundsHandler.setDisplayLayout( + mDisplayController.getDisplayLayout(displayId)); + } + }; /** * Handler for system task stack changes. @@ -228,7 +238,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Override public void onConfigurationChanged() { - mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged()); + mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged(mContext)); } @Override @@ -256,14 +266,12 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Inject public PipManager(Context context, BroadcastDispatcher broadcastDispatcher, @PipMenuActivityClass Class<?> pipMenuActivityClass, + ConfigurationController configController, + DeviceConfigProxy deviceConfig, DisplayController displayController, + Divider divider, FloatingContentCoordinator floatingContentCoordinator, - DeviceConfigProxy deviceConfig, - PipBoundsHandler pipBoundsHandler, - PipSnapAlgorithm pipSnapAlgorithm, - PipTaskOrganizer pipTaskOrganizer, SysUiState sysUiState, - ConfigurationController configController, PipUiEventLogger pipUiEventLogger) { mContext = context; mActivityManager = ActivityManager.getService(); @@ -276,8 +284,11 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); - mPipBoundsHandler = pipBoundsHandler; - mPipTaskOrganizer = pipTaskOrganizer; + mDisplayController = displayController; + mPipBoundsHandler = new PipBoundsHandler(mContext); + mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler, + mPipSurfaceTransactionHelper, divider, mDisplayController, pipUiEventLogger); mPipTaskOrganizer.registerPipTransitionCallback(this); mInputConsumerController = InputConsumerController.getPipInputConsumer(); mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); @@ -285,8 +296,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMediaController, mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, - floatingContentCoordinator, deviceConfig, pipSnapAlgorithm, sysUiState, - pipUiEventLogger); + floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, mTouchHandler.getMotionHelper()); displayController.addDisplayChangingController(mRotationController); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index ecd315b336f2..1b84c1417c51 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -58,7 +58,6 @@ import com.android.systemui.R; import com.android.systemui.model.SysUiState; import com.android.systemui.pip.PipAnimationController; import com.android.systemui.pip.PipBoundsHandler; -import com.android.systemui.pip.PipSnapAlgorithm; import com.android.systemui.pip.PipTaskOrganizer; import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.InputConsumerController; @@ -99,7 +98,6 @@ public class PipTouchHandler { private IPinnedStackController mPinnedStackController; private final PipMenuActivityController mMenuController; - private final PipSnapAlgorithm mSnapAlgorithm; private final AccessibilityManager mAccessibilityManager; private boolean mShowPipMenuOnAnimationEnd = false; @@ -216,20 +214,19 @@ public class PipTouchHandler { PipTaskOrganizer pipTaskOrganizer, FloatingContentCoordinator floatingContentCoordinator, DeviceConfigProxy deviceConfig, - PipSnapAlgorithm pipSnapAlgorithm, SysUiState sysUiState, PipUiEventLogger pipUiEventLogger) { // Initialize the Pip input consumer mContext = context; mActivityManager = activityManager; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); + mPipBoundsHandler = pipBoundsHandler; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mMenuController = menuController; mMenuController.addListener(new PipMenuListener()); - mSnapAlgorithm = pipSnapAlgorithm; mGesture = new DefaultPipTouchGesture(); mMotionHelper = new PipMotionHelper(mContext, pipTaskOrganizer, mMenuController, - mSnapAlgorithm, floatingContentCoordinator); + mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper, deviceConfig, pipTaskOrganizer, this::getMovementBounds, @@ -248,11 +245,10 @@ public class PipTouchHandler { inputConsumerController.setInputListener(this::handleTouchEvent); inputConsumerController.setRegistrationListener(this::onRegistrationChanged); - mPipBoundsHandler = pipBoundsHandler; mFloatingContentCoordinator = floatingContentCoordinator; mConnection = new PipAccessibilityInteractionConnection(mContext, mMotionHelper, - pipTaskOrganizer, pipSnapAlgorithm, this::onAccessibilityShowMenu, - this::updateMovementBounds, mHandler); + pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(), + this::onAccessibilityShowMenu, this::updateMovementBounds, mHandler); mPipUiEventLogger = pipUiEventLogger; @@ -419,7 +415,8 @@ public class PipTouchHandler { public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) { final Rect toMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(outBounds, insetBounds, toMovementBounds, 0); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds, + toMovementBounds, 0); final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets; if ((prevBottom - mBottomOffsetBufferPx) <= curBounds.top) { outBounds.offsetTo(outBounds.left, toMovementBounds.bottom); @@ -450,26 +447,26 @@ public class PipTouchHandler { // Re-calculate the expanded bounds mNormalBounds.set(normalBounds); Rect normalMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalMovementBounds, - bottomOffset); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mNormalBounds, insetBounds, + normalMovementBounds, bottomOffset); if (mMovementBounds.isEmpty()) { // mMovementBounds is not initialized yet and a clean movement bounds without // bottom offset shall be used later in this function. - mSnapAlgorithm.getMovementBounds(curBounds, insetBounds, mMovementBounds, - 0 /* bottomOffset */); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, + mMovementBounds, 0 /* bottomOffset */); } // Calculate the expanded size float aspectRatio = (float) normalBounds.width() / normalBounds.height(); Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); - Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, + Size expandedSize = mPipBoundsHandler.getSnapAlgorithm().getSizeForAspectRatio(aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y); mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight()); Rect expandedMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds, - bottomOffset); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mExpandedBounds, insetBounds, + expandedMovementBounds, bottomOffset); mPipResizeGestureHandler.updateMinSize(mNormalBounds.width(), mNormalBounds.height()); mPipResizeGestureHandler.updateMaxSize(mExpandedBounds.width(), mExpandedBounds.height()); @@ -489,7 +486,7 @@ public class PipTouchHandler { } else { final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu(); final Rect toMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(curBounds, insetBounds, + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, insetBounds, toMovementBounds, mIsImeShowing ? mImeHeight : 0); final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets; // This is to handle landscape fullscreen IMEs, don't apply the extra offset in this @@ -500,8 +497,8 @@ public class PipTouchHandler { if (isExpanded) { curBounds.set(mExpandedBounds); - mSnapAlgorithm.applySnapFraction(curBounds, toMovementBounds, - mSavedSnapFraction); + mPipBoundsHandler.getSnapAlgorithm().applySnapFraction(curBounds, + toMovementBounds, mSavedSnapFraction); } if (prevBottom < toBottom) { @@ -608,7 +605,7 @@ public class PipTouchHandler { .spring(DynamicAnimation.TRANSLATION_Y, mTargetViewContainer.getHeight(), mTargetSpringConfig) - .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) + .withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE)) .start(); ((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition( @@ -844,8 +841,8 @@ public class PipTouchHandler { if (mDeferResizeToNormalBoundsUntilRotation == -1) { Rect restoreBounds = new Rect(getUserResizeBounds()); Rect restoredMovementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(restoreBounds, mInsetBounds, - restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(restoreBounds, + mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction, restoredMovementBounds, mMovementBounds, false /* immediate */); mSavedSnapFraction = -1f; @@ -1025,25 +1022,25 @@ public class PipTouchHandler { mMenuController.hideMenu(); } } - }; + } /** * Updates the current movement bounds based on whether the menu is currently visible and * resized. */ private void updateMovementBounds() { - mSnapAlgorithm.getMovementBounds(mMotionHelper.getBounds(), mInsetBounds, - mMovementBounds, mIsImeShowing ? mImeHeight : 0); + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(mMotionHelper.getBounds(), + mInsetBounds, mMovementBounds, mIsImeShowing ? mImeHeight : 0); mMotionHelper.setCurrentMovementBounds(mMovementBounds); boolean isMenuExpanded = mMenuState == MENU_STATE_FULL; mPipBoundsHandler.setMinEdgeSize( - isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0); + isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize : 0); } private Rect getMovementBounds(Rect curBounds) { Rect movementBounds = new Rect(); - mSnapAlgorithm.getMovementBounds(curBounds, mInsetBounds, + mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(curBounds, mInsetBounds, movementBounds, mIsImeShowing ? mImeHeight : 0); return movementBounds; } @@ -1075,6 +1072,7 @@ public class PipTouchHandler { pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction); pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge); pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets); + mPipBoundsHandler.dump(pw, innerPrefix); mTouchState.dump(pw, innerPrefix); mMotionHelper.dump(pw, innerPrefix); if (mPipResizeGestureHandler != null) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 74dc003bd8b6..3aef0dacad72 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import android.annotation.NonNull; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; import android.app.ActivityTaskManager; @@ -54,11 +55,14 @@ import com.android.systemui.pip.BasePipManager; import com.android.systemui.pip.PipBoundsHandler; import com.android.systemui.pip.PipSurfaceTransactionHelper; import com.android.systemui.pip.PipTaskOrganizer; +import com.android.systemui.pip.PipUiEventLogger; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.stackdivider.Divider; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.wm.shell.common.DisplayController; import java.util.ArrayList; import java.util.List; @@ -111,6 +115,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private Context mContext; private PipBoundsHandler mPipBoundsHandler; private PipTaskOrganizer mPipTaskOrganizer; + private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper; private IActivityTaskManager mActivityTaskManager; private MediaSessionManager mMediaSessionManager; private int mState = STATE_NO_PIP; @@ -229,17 +234,17 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio @Inject public PipManager(Context context, BroadcastDispatcher broadcastDispatcher, - PipBoundsHandler pipBoundsHandler, - PipTaskOrganizer pipTaskOrganizer, - PipSurfaceTransactionHelper surfaceTransactionHelper, - Divider divider) { + ConfigurationController configController, + DisplayController displayController, + Divider divider, + @NonNull PipUiEventLogger pipUiEventLogger) { if (mInitialized) { return; } mInitialized = true; mContext = context; - mPipBoundsHandler = pipBoundsHandler; + mPipBoundsHandler = new PipBoundsHandler(mContext); // Ensure that we have the display info in case we get calls to update the bounds before the // listener calls back final DisplayInfo displayInfo = new DisplayInfo(); @@ -248,7 +253,9 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mResizeAnimationDuration = context.getResources() .getInteger(R.integer.config_pipResizeAnimationDuration); - mPipTaskOrganizer = pipTaskOrganizer; + mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController); + mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler, + mPipSurfaceTransactionHelper, divider, displayController, pipUiEventLogger); mPipTaskOrganizer.registerPipTransitionCallback(this); mActivityTaskManager = ActivityTaskManager.getService(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt index 17e62890aadd..3bfdf5caa9b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt @@ -16,9 +16,9 @@ package com.android.systemui.statusbar.notification.collection +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection 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.listbuilder.pluggable.NotifSection /** * Stores the state that [ShadeListBuilder] assigns to this [ListEntry] @@ -35,7 +35,6 @@ data class ListAttachState private constructor( * parent's section. Null if not attached to the list. */ var section: NotifSection?, - var sectionIndex: Int, /** * If a [NotifFilter] is excluding this entry from the list, then that filter. Always null for @@ -60,7 +59,6 @@ data class ListAttachState private constructor( fun clone(other: ListAttachState) { parent = other.parent section = other.section - sectionIndex = other.sectionIndex excludingFilter = other.excludingFilter promoter = other.promoter suppressedChanges.clone(other.suppressedChanges) @@ -70,7 +68,6 @@ data class ListAttachState private constructor( fun reset() { parent = null section = null - sectionIndex = -1 excludingFilter = null promoter = null suppressedChanges.reset() @@ -82,7 +79,6 @@ data class ListAttachState private constructor( return ListAttachState( null, null, - -1, null, null, SuppressedAttachState.create()) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java index 786c97d03712..52c5c3e08118 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java @@ -112,11 +112,9 @@ public class ListDumper { .append(")"); } - if (entry.getNotifSection() != null) { - sb.append(" sectionIndex=") - .append(entry.getSection()) - .append(" sectionName=") - .append(entry.getNotifSection().getName()); + if (entry.getSection() != null) { + sb.append(" section=") + .append(entry.getSection().getLabel()); } if (includeRecordKeeping) { @@ -175,12 +173,9 @@ public class ListDumper { } if (notifEntry.getAttachState().getSuppressedChanges().getSection() != null) { - rksb.append("suppressedSectionIndex=") + rksb.append("suppressedSection=") .append(notifEntry.getAttachState().getSuppressedChanges() - .getSectionIndex()) - .append(" sectionName=") - .append(notifEntry.getAttachState().getSuppressedChanges() - .getSection().getName()) + .getSection()) .append(" "); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java index 65f5dc4e5f7c..82c1f243dcdb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java @@ -21,7 +21,7 @@ import android.annotation.UptimeMillisLong; import androidx.annotation.Nullable; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; /** * Abstract superclass for top-level entries, i.e. things that can appear in the final notification @@ -78,13 +78,12 @@ public abstract class ListEntry { return mPreviousAttachState.getParent(); } - /** The section this notification was assigned to (0 to N-1, where N is number of sections). */ - public int getSection() { - return mAttachState.getSectionIndex(); + @Nullable public NotifSection getSection() { + return mAttachState.getSection(); } - @Nullable public NotifSection getNotifSection() { - return mAttachState.getSection(); + public int getSectionIndex() { + return mAttachState.getSection() != null ? mAttachState.getSection().getIndex() : -1; } ListAttachState getAttachState() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java index 05dd4df1f2ce..a1844ff5d221 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java @@ -24,7 +24,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; 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.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; @@ -155,10 +155,10 @@ public class NotifPipeline implements CommonNotifCollection { * Sections that are used to sort top-level entries. If two entries have the same section, * NotifComparators are consulted. Sections from this list are called in order for each * notification passed through the pipeline. The first NotifSection to return true for - * {@link NotifSection#isInSection(ListEntry)} sets the entry as part of its Section. + * {@link NotifSectioner#isInSection(ListEntry)} sets the entry as part of its Section. */ - public void setSections(List<NotifSection> sections) { - mShadeListBuilder.setSections(sections); + public void setSections(List<NotifSectioner> sections) { + mShadeListBuilder.setSectioners(sections); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index 6cbebf803511..2b545c56c8bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -28,10 +28,11 @@ import static com.android.systemui.statusbar.notification.collection.listbuilder import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING; +import static java.util.Objects.requireNonNull; + import android.annotation.MainThread; import android.annotation.Nullable; import android.util.ArrayMap; -import android.util.Pair; import androidx.annotation.NonNull; @@ -39,6 +40,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationInteractionTracker; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; @@ -48,7 +50,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeL import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; 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.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; @@ -119,6 +121,8 @@ public class ShadeListBuilder implements Dumpable { mLogger = logger; mInteractionTracker = interactionTracker; dumpManager.registerDumpable(TAG, this); + + setSectioners(Collections.emptyList()); } /** @@ -193,15 +197,17 @@ public class ShadeListBuilder implements Dumpable { promoter.setInvalidationListener(this::onPromoterInvalidated); } - void setSections(List<NotifSection> sections) { + void setSectioners(List<NotifSectioner> sectioners) { Assert.isMainThread(); mPipelineState.requireState(STATE_IDLE); mNotifSections.clear(); - for (NotifSection section : sections) { - mNotifSections.add(section); - section.setInvalidationListener(this::onNotifSectionInvalidated); + for (NotifSectioner sectioner : sectioners) { + mNotifSections.add(new NotifSection(sectioner, mNotifSections.size())); + sectioner.setInvalidationListener(this::onNotifSectionInvalidated); } + + mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size())); } void setNotifStabilityManager(NotifStabilityManager notifStabilityManager) { @@ -275,7 +281,7 @@ public class ShadeListBuilder implements Dumpable { rebuildListIfBefore(STATE_TRANSFORMING); } - private void onNotifSectionInvalidated(NotifSection section) { + private void onNotifSectionInvalidated(NotifSectioner section) { Assert.isMainThread(); mLogger.logNotifSectionInvalidated(section.getName(), mPipelineState.getState()); @@ -652,7 +658,6 @@ public class ShadeListBuilder implements Dumpable { */ private void annulAddition(ListEntry entry) { entry.setParent(null); - entry.getAttachState().setSectionIndex(-1); entry.getAttachState().setSection(null); entry.getAttachState().setPromoter(null); if (entry.mFirstAddedIteration == mIterationCount) { @@ -663,12 +668,12 @@ public class ShadeListBuilder implements Dumpable { private void sortList() { // Assign sections to top-level elements and sort their children for (ListEntry entry : mNotifList) { - Pair<NotifSection, Integer> sectionWithIndex = applySections(entry); + NotifSection section = applySections(entry); if (entry instanceof GroupEntry) { GroupEntry parent = (GroupEntry) entry; for (NotificationEntry child : parent.getChildren()) { - child.getAttachState().setSection(sectionWithIndex.first); - child.getAttachState().setSectionIndex(sectionWithIndex.second); + child.getAttachState().setSection(section); + child.getAttachState().setSection(section); } parent.sortChildren(sChildComparator); } @@ -736,16 +741,13 @@ public class ShadeListBuilder implements Dumpable { mLogger.logSectionChanged( mIterationCount, prev.getSection(), - prev.getSectionIndex(), - curr.getSection(), - curr.getSectionIndex()); + curr.getSection()); } if (curr.getSuppressedChanges().getSection() != null) { mLogger.logSectionChangeSuppressed( mIterationCount, curr.getSuppressedChanges().getSection(), - curr.getSuppressedChanges().getSectionIndex(), curr.getSection()); } } @@ -762,7 +764,10 @@ public class ShadeListBuilder implements Dumpable { callOnCleanup(mNotifPromoters); callOnCleanup(mNotifFinalizeFilters); callOnCleanup(mNotifComparators); - callOnCleanup(mNotifSections); + + for (int i = 0; i < mNotifSections.size(); i++) { + mNotifSections.get(i).getSectioner().onCleanup(); + } if (mNotifStabilityManager != null) { callOnCleanup(List.of(mNotifStabilityManager)); @@ -777,7 +782,9 @@ public class ShadeListBuilder implements Dumpable { private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> { - int cmp = Integer.compare(o1.getSection(), o2.getSection()); + int cmp = Integer.compare( + requireNonNull(o1.getSection()).getIndex(), + requireNonNull(o2.getSection()).getIndex()); if (cmp == 0) { for (int i = 0; i < mNotifComparators.size(); i++) { @@ -855,45 +862,41 @@ public class ShadeListBuilder implements Dumpable { return null; } - private Pair<NotifSection, Integer> applySections(ListEntry entry) { - Pair<NotifSection, Integer> sectionWithIndex = findSection(entry); + private NotifSection applySections(ListEntry entry) { + final NotifSection newSection = findSection(entry); final ListAttachState prevAttachState = entry.getPreviousAttachState(); + NotifSection finalSection = newSection; + // are we changing sections of this entry? if (mNotifStabilityManager != null && prevAttachState.getParent() != null - && (sectionWithIndex.first != prevAttachState.getSection() - || sectionWithIndex.second != prevAttachState.getSectionIndex())) { + && newSection != prevAttachState.getSection()) { // are section changes allowed? - if (!mNotifStabilityManager.isSectionChangeAllowed( - entry.getRepresentativeEntry())) { - entry.getAttachState().getSuppressedChanges().setSection( - sectionWithIndex.first); - entry.getAttachState().getSuppressedChanges().setSectionIndex( - sectionWithIndex.second); + if (!mNotifStabilityManager.isSectionChangeAllowed(entry.getRepresentativeEntry())) { + // record the section that we wanted to change to + entry.getAttachState().getSuppressedChanges().setSection(newSection); // keep the previous section - sectionWithIndex = new Pair( - prevAttachState.getSection(), - prevAttachState.getSectionIndex()); + finalSection = prevAttachState.getSection(); } } - entry.getAttachState().setSection(sectionWithIndex.first); - entry.getAttachState().setSectionIndex(sectionWithIndex.second); + entry.getAttachState().setSection(finalSection); - return sectionWithIndex; + return finalSection; } - private Pair<NotifSection, Integer> findSection(ListEntry entry) { + @NonNull + private NotifSection findSection(ListEntry entry) { for (int i = 0; i < mNotifSections.size(); i++) { - NotifSection sectioner = mNotifSections.get(i); - if (sectioner.isInSection(entry)) { - return new Pair<>(sectioner, i); + NotifSection section = mNotifSections.get(i); + if (section.getSectioner().isInSection(entry)) { + return section; } } - return new Pair<>(sDefaultSection, mNotifSections.size()); + throw new RuntimeException("Missing default sectioner!"); } private void rebuildListIfBefore(@PipelineState.StateName int state) { @@ -963,15 +966,15 @@ public class ShadeListBuilder implements Dumpable { void onRenderList(@NonNull List<ListEntry> entries); } - private static final NotifSection sDefaultSection = - new NotifSection("UnknownSection") { + private static final NotifSectioner DEFAULT_SECTIONER = + new NotifSectioner("UnknownSection") { @Override public boolean isInSection(ListEntry entry) { return true; } }; - private static final String TAG = "ShadeListBuilder"; - private static final int MIN_CHILDREN_FOR_GROUP = 2; + + private static final String TAG = "ShadeListBuilder"; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt index 52612365712e..3eb2e610f329 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.notification.collection -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection /** * Stores the suppressed state that [ShadeListBuilder] assigned to this [ListEntry] before the @@ -33,22 +33,19 @@ data class SuppressedAttachState private constructor( * The assigned section for this ListEntry. If the child of the group, this will be the * parent's section. Null if not attached to the list. */ - var section: NotifSection?, - var sectionIndex: Int + var section: NotifSection? ) { /** Copies the state of another instance. */ fun clone(other: SuppressedAttachState) { parent = other.parent section = other.section - sectionIndex = other.sectionIndex } /** Resets back to a "clean" state (the same as created by the factory method) */ fun reset() { parent = null section = null - sectionIndex = -1 } companion object { @@ -56,8 +53,7 @@ data class SuppressedAttachState private constructor( fun create(): SuppressedAttachState { return SuppressedAttachState( null, - null, - -1) + null) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 0b9bded5ef58..c7ac40346ce1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -29,7 +29,7 @@ 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.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -83,8 +83,8 @@ public class AppOpsCoordinator implements Coordinator { } - public NotifSection getSection() { - return mNotifSection; + public NotifSectioner getSectioner() { + return mNotifSectioner; } /** @@ -179,7 +179,7 @@ public class AppOpsCoordinator implements Coordinator { /** * Puts foreground service notifications into its own section. */ - private final NotifSection mNotifSection = new NotifSection("ForegroundService") { + private final NotifSectioner mNotifSectioner = new NotifSectioner("ForegroundService") { @Override public boolean isInSection(ListEntry entry) { NotificationEntry notificationEntry = entry.getRepresentativeEntry(); 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 c8e859f27a5c..dea11626a3f8 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 @@ -21,7 +21,7 @@ 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.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import javax.inject.Inject @@ -42,7 +42,7 @@ class ConversationCoordinator @Inject constructor( } } - private val mNotifSection: NotifSection = object : NotifSection("People") { + val sectioner = object : NotifSectioner("People") { override fun isInSection(entry: ListEntry): Boolean { return isConversation(entry.representativeEntry!!) } @@ -52,10 +52,6 @@ class ConversationCoordinator @Inject constructor( pipeline.addPromoter(notificationPromoter) } - fun getSection(): NotifSection { - return mNotifSection - } - private fun isConversation(entry: NotificationEntry): Boolean = peopleNotificationIdentifier.getPeopleNotificationType(entry.sbn, entry.ranking) != TYPE_NON_PERSON diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java index 6e6cecaf62fa..c023400ca9ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java @@ -27,7 +27,7 @@ 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.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder; @@ -88,8 +88,8 @@ public class HeadsUpCoordinator implements Coordinator { pipeline.addNotificationLifetimeExtender(mLifetimeExtender); } - public NotifSection getSection() { - return mNotifSection; + public NotifSectioner getSectioner() { + return mNotifSectioner; } private void onHeadsUpViewBound(NotificationEntry entry) { @@ -191,7 +191,7 @@ public class HeadsUpCoordinator implements Coordinator { } }; - private final NotifSection mNotifSection = new NotifSection("HeadsUp") { + private final NotifSectioner mNotifSectioner = new NotifSectioner("HeadsUp") { @Override public boolean isInSection(ListEntry entry) { return isCurrentlyShowingHun(entry); @@ -207,7 +207,7 @@ public class HeadsUpCoordinator implements Coordinator { endNotifLifetimeExtension(); mCurrentHun = newHUN; mNotifPromoter.invalidateList(); - mNotifSection.invalidateList(); + mNotifSectioner.invalidateList(); } if (!isHeadsUp) { mHeadsUpViewBinder.unbindHeadsUpView(entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index 87ca717982f5..ded5e46593f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -21,7 +21,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; @@ -41,7 +41,7 @@ import javax.inject.Inject; public class NotifCoordinators implements Dumpable { private static final String TAG = "NotifCoordinators"; private final List<Coordinator> mCoordinators = new ArrayList<>(); - private final List<NotifSection> mOrderedSections = new ArrayList<>(); + private final List<NotifSectioner> mOrderedSections = new ArrayList<>(); /** * Creates all the coordinators. @@ -81,12 +81,12 @@ public class NotifCoordinators implements Dumpable { // Manually add Ordered Sections // HeadsUp > FGS > People > Alerting > Silent > Unknown/Default if (featureFlags.isNewNotifPipelineRenderingEnabled()) { - mOrderedSections.add(headsUpCoordinator.getSection()); // HeadsUp + mOrderedSections.add(headsUpCoordinator.getSectioner()); // HeadsUp } - mOrderedSections.add(appOpsCoordinator.getSection()); // ForegroundService - mOrderedSections.add(conversationCoordinator.getSection()); // People - mOrderedSections.add(rankingCoordinator.getAlertingSection()); // Alerting - mOrderedSections.add(rankingCoordinator.getSilentSection()); // Silent + mOrderedSections.add(appOpsCoordinator.getSectioner()); // ForegroundService + mOrderedSections.add(conversationCoordinator.getSectioner()); // People + mOrderedSections.add(rankingCoordinator.getAlertingSectioner()); // Alerting + mOrderedSections.add(rankingCoordinator.getSilentSectioner()); // Silent } /** @@ -109,7 +109,7 @@ public class NotifCoordinators implements Dumpable { pw.println("\t" + c.getClass()); } - for (NotifSection s : mOrderedSections) { + for (NotifSectioner s : mOrderedSections) { pw.println("\t" + s.getName()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index a32b1636057b..0f08e0ff491c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -22,7 +22,7 @@ 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.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import javax.inject.Inject; @@ -57,22 +57,22 @@ public class RankingCoordinator implements Coordinator { pipeline.addPreGroupFilter(mDozingFilter); } - public NotifSection getAlertingSection() { - return mAlertingNotifSection; + public NotifSectioner getAlertingSectioner() { + return mAlertingNotifSectioner; } - public NotifSection getSilentSection() { - return mSilentNotifSection; + public NotifSectioner getSilentSectioner() { + return mSilentNotifSectioner; } - private final NotifSection mAlertingNotifSection = new NotifSection("Alerting") { + private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting") { @Override public boolean isInSection(ListEntry entry) { return mHighPriorityProvider.isHighPriority(entry); } }; - private final NotifSection mSilentNotifSection = new NotifSection("Silent") { + private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent") { @Override public boolean isInSection(ListEntry entry) { return !mHighPriorityProvider.isHighPriority(entry); diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/dagger/OneHandedModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt index fe5fa2b5fccd..c09122ea3c26 100644 --- a/packages/SystemUI/src/com/android/systemui/onehanded/dagger/OneHandedModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt @@ -14,23 +14,14 @@ * limitations under the License. */ -package com.android.systemui.onehanded.dagger; +package com.android.systemui.statusbar.notification.collection.listbuilder -import com.android.systemui.onehanded.OneHandedManager; -import com.android.systemui.onehanded.OneHandedManagerImpl; - -import dagger.Binds; -import dagger.Module; - -/** - * Dagger Module for One handed. - */ -@Module -public abstract class OneHandedModule { - - /** Binds OneHandedManager as the default. */ - @Binds - public abstract OneHandedManager provideOneHandedManager( - OneHandedManagerImpl oneHandedManagerImpl); +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner +data class NotifSection( + val sectioner: NotifSectioner, + val index: Int +) { + val label: String + get() = "Section($index, \"${sectioner.name}\")" } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt index f7bfeb7234f0..9ee7db738c20 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry 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.listbuilder.pluggable.NotifSection import javax.inject.Inject class ShadeListBuilderLogger @Inject constructor( @@ -211,21 +210,17 @@ class ShadeListBuilderLogger @Inject constructor( fun logSectionChanged( buildId: Int, prevSection: NotifSection?, - prevIndex: Int, - newSection: NotifSection?, - newIndex: Int + newSection: NotifSection? ) { buffer.log(TAG, INFO, { long1 = buildId.toLong() - str1 = prevSection?.name - int1 = prevIndex - str2 = newSection?.name - int2 = newIndex + str1 = prevSection?.label + str2 = newSection?.label }, { if (str1 == null) { - "(Build $long1) Section assigned: '$str2' (#$int2)" + "(Build $long1) Section assigned: $str2" } else { - "(Build $long1) Section changed: '$str1' (#$int1) -> '$str2' (#$int2)" + "(Build $long1) Section changed: $str1 -> $str2" } }) } @@ -233,17 +228,14 @@ class ShadeListBuilderLogger @Inject constructor( fun logSectionChangeSuppressed( buildId: Int, suppressedSection: NotifSection?, - suppressedSectionIndex: Int, assignedSection: NotifSection? ) { buffer.log(TAG, INFO, { long1 = buildId.toLong() - str1 = suppressedSection?.name - int1 = suppressedSectionIndex - str2 = assignedSection?.name + str1 = suppressedSection?.label + str2 = assignedSection?.label }, { - "(Build $long1) Section change suppressed: '$str1' (#$int1). " + - "Keeping section: '$str2'" + "(Build $long1) Suppressing section change to $str1 (staying at $str2)" }) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java index fe5ba3c8e6fc..b57f504189f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java @@ -22,8 +22,8 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder; /** * Pluggable for participating in notif sectioning. See {@link ShadeListBuilder#setSections}. */ -public abstract class NotifSection extends Pluggable<NotifSection> { - protected NotifSection(String name) { +public abstract class NotifSectioner extends Pluggable<NotifSectioner> { + protected NotifSectioner(String name) { super(name); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt index e8124944bcb0..a1800ed12125 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt @@ -25,10 +25,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain * we should just modify NLC to implement the NodeController interface. */ class RootNodeController( - private val listContainer: NotificationListContainer + private val listContainer: NotificationListContainer, + override val view: View ) : NodeController { override val nodeLabel: String = "<root>" - override val view: View = listContainer as View override fun getChildAt(index: Int): View? { return listContainer.getContainerChildAt(index) 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 118ff4a9fbb7..3c35b7bd8472 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 @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.collection.render +import android.content.Context +import android.view.View import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -30,12 +32,15 @@ import javax.inject.Inject * currently populate the notification shade. */ class ShadeViewManager constructor( + context: Context, listContainer: NotificationListContainer, logger: ShadeViewDifferLogger, private val viewBarn: NotifViewBarn, private val notificationIconAreaController: NotificationIconAreaController ) { - private val rootController = RootNodeController(listContainer) + // We pass a shim view here because the listContainer may not actually have a view associated + // with it and the differ never actually cares about the root node's view. + private val rootController = RootNodeController(listContainer, View(context)) private val viewDiffer = ShadeViewDiffer(rootController, logger) fun attach(listBuilder: ShadeListBuilder) { @@ -82,11 +87,17 @@ class ShadeViewManager constructor( } class ShadeViewManagerFactory @Inject constructor( + private val context: Context, private val logger: ShadeViewDifferLogger, private val viewBarn: NotifViewBarn, private val notificationIconAreaController: NotificationIconAreaController ) { fun create(listContainer: NotificationListContainer): ShadeViewManager { - return ShadeViewManager(listContainer, logger, viewBarn, notificationIconAreaController) + return ShadeViewManager( + context, + listContainer, + logger, + viewBarn, + notificationIconAreaController) } } 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 d4c270f45ceb..e061472b3939 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 @@ -95,21 +95,15 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SwipeHelper; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; @@ -145,7 +139,6 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; 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.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -154,11 +147,8 @@ import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.ScrollAdapter; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.Assert; @@ -178,7 +168,7 @@ import javax.inject.Named; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. */ -public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter, Dumpable { +public class NotificationStackScrollLayout extends ViewGroup implements Dumpable { public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "StackScroller"; @@ -195,10 +185,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * gap is drawn between them). In this case we don't want to round their corners. */ private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1; - private final KeyguardBypassController mKeyguardBypassController; + private OnMenuEventListener mMenuEventListener; + private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; private final DynamicPrivacyController mDynamicPrivacyController; private final SysuiStatusBarStateController mStatusbarStateController; - private final KeyguardMediaController mKeyguardMediaController; private ExpandHelper mExpandHelper; private final NotificationSwipeHelper mSwipeHelper; @@ -249,6 +239,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mBottomMargin; private int mBottomInset = 0; private float mQsExpansionFraction; + private int mCurrentUserId; /** * The algorithm which calculates the properties for our children @@ -326,7 +317,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * motion. */ private int mMaxScrollAfterExpand; - private ExpandableNotificationRow.LongPressListener mLongPressListener; boolean mCheckForLeavebehind; /** @@ -348,12 +338,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return true; } }; - private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() { - @Override - public void onUserChanged(int userId) { - updateSensitiveness(false /* animated */); - } - }; + private StatusBar mStatusBar; private int[] mTempInt2 = new int[2]; private boolean mGenerateChildOrderChangedEvent; @@ -368,7 +353,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean mForceNoOverlappingRendering; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); private FalsingManager mFalsingManager; - private final ZenModeController mZenController; private boolean mAnimationRunning; private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater = new ViewTreeObserver.OnPreDrawListener() { @@ -498,7 +482,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; private HeadsUpAppearanceController mHeadsUpAppearanceController; - private final NotificationLockscreenUserManager mLockscreenUserManager; private final Rect mTmpRect = new Rect(); private final FeatureFlags mFeatureFlags; private final NotifPipeline mNotifPipeline; @@ -511,7 +494,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd protected final UiEventLogger mUiEventLogger; private final NotificationRemoteInputManager mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); - private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class); private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class); private final LockscreenGestureLogger mLockscreenGestureLogger = @@ -536,11 +518,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mWaterfallTopInset; private NotificationStackScrollLayoutController mController; - private SysuiColorExtractor.OnColorsChangedListener mOnColorsChangedListener = - (colorExtractor, which) -> { - final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); - updateDecorViews(useDarkText); - }; + private boolean mKeyguardMediaControllorVisible; private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = new ExpandableView.OnHeightChangedListener() { @@ -555,20 +533,44 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } }; + private final ScrollAdapter mScrollAdapter = new ScrollAdapter() { + @Override + public boolean isScrolledToTop() { + if (ANCHOR_SCROLLING) { + updateScrollAnchor(); + // TODO: once we're recycling this will need to check the adapter position of the + // child + return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0; + } else { + return mOwnScrollY == 0; + } + } + + @Override + public boolean isScrolledToBottom() { + if (ANCHOR_SCROLLING) { + return getMaxPositiveScrollAmount() <= 0; + } else { + return mOwnScrollY >= getScrollRange(); + } + } + + @Override + public View getHostView() { + return NotificationStackScrollLayout.this; + } + }; + @Inject public NotificationStackScrollLayout( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, NotificationRoundnessManager notificationRoundnessManager, DynamicPrivacyController dynamicPrivacyController, - SysuiStatusBarStateController statusBarStateController, + SysuiStatusBarStateController statusbarStateController, HeadsUpManagerPhone headsUpManager, - KeyguardBypassController keyguardBypassController, - KeyguardMediaController keyguardMediaController, FalsingManager falsingManager, - NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGutsManager notificationGutsManager, - ZenModeController zenController, NotificationSectionsManager notificationSectionsManager, ForegroundServiceSectionController fgsSectionController, ForegroundServiceDismissalFeatureController fgsFeatureController, @@ -583,13 +585,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mRoundnessManager = notificationRoundnessManager; - mLockscreenUserManager = notificationLockscreenUserManager; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed); - mKeyguardBypassController = keyguardBypassController; mFalsingManager = falsingManager; - mZenController = zenController; mFgsSectionController = fgsSectionController; mSectionsManager = notificationSectionsManager; @@ -608,16 +607,33 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback, minHeight, maxHeight); mExpandHelper.setEventSource(this); - mExpandHelper.setScrollAdapter(this); + mExpandHelper.setScrollAdapter(mScrollAdapter); + + // TODO: move swipe helper into controller. + // The anonymous proxy through to mMenuEventListener is temporary until more can be moved + // into the controller. mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback, - getContext(), mMenuEventListener, mFalsingManager); + getContext(), new OnMenuEventListener() { + @Override + public void onMenuClicked(View row, int x, int y, MenuItem menu) { + mMenuEventListener.onMenuClicked(row, x, y, menu); + } + + @Override + public void onMenuReset(View row) { + mMenuEventListener.onMenuReset(row); + } + + @Override + public void onMenuShown(View row) { + mMenuEventListener.onMenuShown(row); + } + }, mFalsingManager); mStackScrollAlgorithm = createStackScrollAlgorithm(context); - initView(context); mShouldDrawNotificationBackground = res.getBoolean(R.bool.config_drawNotificationBackground); mFadeNotificationsOnDismiss = res.getBoolean(R.bool.config_fadeNotificationsOnDismiss); - mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); setOutlineProvider(mOutlineProvider); // Blocking helper manager wants to know the expanded state, update as well. @@ -667,20 +683,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } mDynamicPrivacyController = dynamicPrivacyController; - mStatusbarStateController = statusBarStateController; + mStatusbarStateController = statusbarStateController; initializeForegroundServiceSection(fgsFeatureController); mUiEventLogger = uiEventLogger; - mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); - mKeyguardMediaController = keyguardMediaController; - keyguardMediaController.setVisibilityChangedListener((visible) -> { - if (visible) { - generateAddAnimation(keyguardMediaController.getView(), false /*fromMoreCard */); - } else { - generateRemoveAnimation(keyguardMediaController.getView()); - } - requestChildrenUpdate(); - return null; - }); } private void initializeForegroundServiceSection( @@ -719,7 +724,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd public float getWakeUpHeight() { ExpandableView firstChild = getFirstChildWithBackground(); if (firstChild != null) { - if (mKeyguardBypassController.getBypassEnabled()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled()) { return firstChild.getHeadsUpHeightWithoutHeader(); } else { return firstChild.getCollapsedHeight(); @@ -794,21 +799,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd }; } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) - .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); - } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public NotificationSwipeActionHelper getSwipeActionHelper() { return mSwipeHelper; @@ -898,7 +888,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } boolean shouldDrawBackground; - if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) { shouldDrawBackground = isPulseExpanding(); } else { shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild; @@ -1013,9 +1003,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } + private void reinitView() { + initView(getContext(), mKeyguardBypassEnabledProvider); + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void initView(Context context) { + void initView(Context context, + KeyguardBypassEnabledProvider keyguardBypassEnabledProvider) { mScroller = new OverScroller(getContext()); + mKeyguardBypassEnabledProvider = keyguardBypassEnabledProvider; + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setClipChildren(false); final ViewConfiguration configuration = ViewConfiguration.get(context); @@ -1227,7 +1224,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd float end = start + child.getActualHeight(); boolean clip = clipStart > start && clipStart < end || clipEnd >= start && clipEnd <= end; - clip &= !(first && isScrolledToTop()); + clip &= !(first && mScrollAdapter.isScrolledToTop()); child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : ExpandableView.NO_ROUNDNESS); first = false; @@ -1287,7 +1284,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void requestChildrenUpdate() { + void requestChildrenUpdate() { if (!mChildrenUpdateRequested) { getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater); mChildrenUpdateRequested = true; @@ -1417,7 +1414,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private void notifyAppearChangedListeners() { float appear; float expandAmount; - if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) { appear = calculateAppearFractionBypass(); expandAmount = getPulseHeight(); } else { @@ -1819,7 +1816,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mSwipeHelper.setDensityScale(densityScale); float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop(); mSwipeHelper.setPagingTouchSlop(pagingTouchSlop); - initView(getContext()); + reinitView(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -2220,7 +2217,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd springBack(); } else { float overScrollTop = getCurrentOverScrollAmount(true /* top */); - if (isScrolledToTop() && mScrollAnchorViewY > 0) { + if (mScrollAdapter.isScrolledToTop() && mScrollAnchorViewY > 0) { notifyOverscrollTopListener(mScrollAnchorViewY, isRubberbanded(true /* onTop */)); } else { @@ -2268,7 +2265,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void springBack() { if (ANCHOR_SCROLLING) { - boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0; + boolean overScrolledTop = mScrollAdapter.isScrolledToTop() && mScrollAnchorViewY > 0; int maxPositiveScrollAmount = getMaxPositiveScrollAmount(); boolean overscrolledBottom = maxPositiveScrollAmount < 0; if (overScrolledTop || overscrolledBottom) { @@ -2545,8 +2542,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private void updateForwardAndBackwardScrollability() { - boolean forwardScrollable = mScrollable && !isScrolledToBottom(); - boolean backwardsScrollable = mScrollable && !isScrolledToTop(); + boolean forwardScrollable = mScrollable && !mScrollAdapter.isScrolledToBottom(); + boolean backwardsScrollable = mScrollable && !mScrollAdapter.isScrolledToTop(); boolean changed = forwardScrollable != mForwardScrollable || backwardsScrollable != mBackwardScrollable; mForwardScrollable = forwardScrollable; @@ -2667,7 +2664,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1 && (mAmbientState.isDozing() - || (mKeyguardBypassController.getBypassEnabled() && onKeyguard)); + || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard)); for (NotificationSection section : mSections) { int minBottomPosition = minTopPosition; if (section == lastSection) { @@ -2830,7 +2827,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mLastScrollerY = 0; // x velocity is set to 1 to avoid overscroller bug mScroller.fling(0, 0, 1, velocityY, 0, 0, minY, maxY, 0, - mExpandedInThisMotion && !isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2); + mExpandedInThisMotion + && !mScrollAdapter.isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2); } } @@ -2937,7 +2935,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } else { mTopPaddingOverflow = 0; } - setTopPadding(topPadding, animate && !mKeyguardBypassController.getBypassEnabled()); + setTopPadding(topPadding, animate && !mKeyguardBypassEnabledProvider.getBypassEnabled()); setExpandedHeight(mExpandedHeight); } @@ -3095,7 +3093,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * @return Whether an animation was generated. */ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private boolean generateRemoveAnimation(ExpandableView child) { + boolean generateRemoveAnimation(ExpandableView child) { if (removeRemovedChildFromHeadsUpChangeAnimations(child)) { mAddedHeadsUpChildren.remove(child); return false; @@ -3332,7 +3330,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - void onViewAddedInternal(ExpandableView child) { + private void onViewAddedInternal(ExpandableView child) { updateHideSensitiveForChild(child); child.setOnHeightChangedListener(mOnChildHeightChangedListener); generateAddAnimation(child, false /* fromMoreCard */); @@ -3343,7 +3341,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } if (ANCHOR_SCROLLING) { // TODO: once we're recycling this will need to check the adapter position of the child - if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) { + if (child == getFirstChildNotGone() + && (mScrollAdapter.isScrolledToTop() || !mIsExpanded)) { // New child was added at the top while we're scrolled to the top; // make it the new anchor view so that we stay at the top. mScrollAnchorView = child; @@ -3361,6 +3360,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd onViewRemovedInternal(row, childrenContainer); } + public void notifyGroupChildAdded(ExpandableView row) { + onViewAddedInternal(row); + } + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void setAnimationsEnabled(boolean animationsEnabled) { mAnimationsEnabled = animationsEnabled; @@ -3533,7 +3536,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd boolean performDisappearAnimation = !mIsExpanded // Only animate if we still have pinned heads up, otherwise we just have the // regular collapse animation of the lock screen - || (mKeyguardBypassController.getBypassEnabled() && onKeyguard() + || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard() && mHeadsUpManager.hasPinnedHeadsUp()); if (performDisappearAnimation && !isHeadsUp) { type = row.wasJustClicked() @@ -3769,11 +3772,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return y < getHeight() - getEmptyBottomMargin(); } - @ShadeViewRefactor(RefactorComponent.INPUT) - public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) { - mLongPressListener = listener; - } - private float getTouchSlop(MotionEvent event) { // Adjust the touch slop if another gesture may be being performed. return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE @@ -4227,7 +4225,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd case MotionEvent.ACTION_DOWN: { final int y = (int) ev.getY(); - mScrolledToTopOnFirstDown = isScrolledToTop(); + mScrolledToTopOnFirstDown = mScrollAdapter.isScrolledToTop(); final ExpandableView childAtTouchPos = getChildAtPosition( ev.getX(), y, false /* requireMinHeight */, false /* ignoreDecors */); if (childAtTouchPos == null) { @@ -4411,32 +4409,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @Override - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public boolean isScrolledToTop() { - if (ANCHOR_SCROLLING) { - updateScrollAnchor(); - // TODO: once we're recycling this will need to check the adapter position of the child - return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0; - } else { - return mOwnScrollY == 0; - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public boolean isScrolledToBottom() { - if (ANCHOR_SCROLLING) { - return getMaxPositiveScrollAmount() <= 0; - } else { - return mOwnScrollY >= getScrollRange(); - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public View getHostView() { - return this; + boolean isScrolledToBottom() { + return mScrollAdapter.isScrolledToBottom(); } @ShadeViewRefactor(RefactorComponent.COORDINATOR) @@ -4734,8 +4708,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void updateSensitiveness(boolean animate) { - boolean hideSensitive = mLockscreenUserManager.isAnyProfilePublicMode(); + void updateSensitiveness(boolean animate, boolean hideSensitive) { if (hideSensitive != mAmbientState.isHideSensitive()) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -4927,7 +4900,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd // Since we are clipping to the outline we need to make sure that the shadows aren't // clipped when pulsing float ownTranslationZ = 0; - if (mKeyguardBypassController.getBypassEnabled() && mAmbientState.isHiddenAtAll()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && mAmbientState.isHiddenAtAll()) { ExpandableView firstChildNotGone = getFirstChildNotGone(); if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) { ownTranslationZ = firstChildNotGone.getTranslationZ(); @@ -4992,11 +4965,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void updateEmptyShadeView(boolean visible) { + void updateEmptyShadeView(boolean visible, boolean notifVisibleInShade) { mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled); int oldTextRes = mEmptyShadeView.getTextResource(); - int newTextRes = mZenController.areNotificationsHiddenInShade() + int newTextRes = notifVisibleInShade ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text; if (oldTextRes != newTextRes) { mEmptyShadeView.setText(newTextRes); @@ -5093,7 +5066,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void requestAnimateEverything() { + void requestAnimateEverything() { if (mIsExpanded && mAnimationsEnabled) { mEverythingNeedsAnimation = true; mNeedsAnimation = true; @@ -5431,17 +5404,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mAmbientState.setStatusBarState(statusBarState); } - private void onStatePostChange() { + void onStatePostChange(boolean fromShadeLocked) { boolean onKeyguard = onKeyguard(); + mAmbientState.setActivatedChild(null); + mAmbientState.setDimmed(onKeyguard); + if (mHeadsUpAppearanceController != null) { mHeadsUpAppearanceController.onStateChanged(); } - SysuiStatusBarStateController state = (SysuiStatusBarStateController) - Dependency.get(StatusBarStateController.class); - updateSensitiveness(state.goingToFullShade() /* animate */); - setDimmed(onKeyguard, state.fromShadeLocked() /* animate */); + setDimmed(onKeyguard, fromShadeLocked); setExpandingEnabled(!onKeyguard); ActivatableNotificationView activatedChild = getActivatedChild(); setActivatedChild(null); @@ -5771,7 +5744,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd */ public float setPulseHeight(float height) { mAmbientState.setPulseHeight(height); - if (mKeyguardBypassController.getBypassEnabled()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled()) { notifyAppearChangedListeners(); } requestChildrenUpdate(); @@ -5849,6 +5822,31 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return mController; } + void setCurrentUserid(int userId) { + mCurrentUserId = userId; + } + + void onMenuShown(View row) { + mSwipeHelper.onMenuShown(row); + } + + void onMenuReset(View row) { + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + if (translatingParentView != null && row == translatingParentView) { + mSwipeHelper.clearExposedMenuView(); + mSwipeHelper.clearTranslatingParentView(); + if (row instanceof ExpandableNotificationRow) { + mHeadsUpManager.setMenuShown( + ((ExpandableNotificationRow) row).getEntry(), false); + + } + } + } + + void setMenuEventListener(OnMenuEventListener menuEventListener) { + mMenuEventListener = menuEventListener; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -6204,89 +6202,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private final StateListener mStateListener = new StateListener() { - @Override - public void onStatePreChange(int oldState, int newState) { - if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) { - requestAnimateEverything(); - } - } - @Override - public void onStateChanged(int newState) { - setStatusBarState(newState); - } - - @Override - public void onStatePostChange() { - NotificationStackScrollLayout.this.onStatePostChange(); - } - }; - - @VisibleForTesting - @ShadeViewRefactor(RefactorComponent.INPUT) - protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { - @Override - public void onMenuClicked(View view, int x, int y, MenuItem item) { - if (mLongPressListener == null) { - return; - } - if (view instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) - .setType(MetricsEvent.TYPE_ACTION) - ); - } - mLongPressListener.onLongPress(view, x, y, item); - } - - @Override - public void onMenuReset(View row) { - View translatingParentView = mSwipeHelper.getTranslatingParentView(); - if (translatingParentView != null && row == translatingParentView) { - mSwipeHelper.clearExposedMenuView(); - mSwipeHelper.clearTranslatingParentView(); - if (row instanceof ExpandableNotificationRow) { - mHeadsUpManager.setMenuShown( - ((ExpandableNotificationRow) row).getEntry(), false); - - } - } - } - - @Override - public void onMenuShown(View row) { - if (row instanceof ExpandableNotificationRow) { - ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; - mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) - .setType(MetricsEvent.TYPE_ACTION)); - mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); - mSwipeHelper.onMenuShown(row); - mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, - false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - - // Check to see if we want to go directly to the notfication guts - NotificationMenuRowPlugin provider = notificationRow.getProvider(); - if (provider.shouldShowGutsOnSnapOpen()) { - MenuItem item = provider.menuItemToExposeOnSnap(); - if (item != null) { - Point origin = provider.getRevealAnimationOrigin(); - mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); - } else { - Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " - + "menu item in menuItemtoExposeOnSnap. Skipping."); - } - - // Close the menu row since we went directly to the guts - resetExposedMenuView(false, true); - } - } - } - }; @ShadeViewRefactor(RefactorComponent.INPUT) private final NotificationSwipeHelper.NotificationCallback mNotificationCallback = @@ -6517,7 +6433,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @SelectedRows int selectedRows) { if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { if (selectedRows == ROWS_ALL) { - mNotifCollection.dismissAllNotifications(mLockscreenUserManager.getCurrentUserId()); + mNotifCollection.dismissAllNotifications(mCurrentUserId); } else { final List<Pair<NotificationEntry, DismissedByUserStats>> entriesWithRowsDismissedFromShade = new ArrayList<>(); @@ -6546,7 +6462,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } if (selectedRows == ROWS_ALL) { try { - mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId()); + mBarService.onClearAllNotifications(mCurrentUserId); } catch (Exception ex) { } } @@ -6568,6 +6484,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd NotificationLogger.getNotificationLocation(entry))); } + public void setKeyguardMediaControllorVisible(boolean keyguardMediaControllorVisible) { + mKeyguardMediaControllorVisible = keyguardMediaControllorVisible; + } + // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ @ShadeViewRefactor(RefactorComponent.INPUT) @@ -6576,8 +6496,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd /* Only ever called as a consequence of a lockscreen expansion gesture. */ @Override public boolean onDraggedDown(View startingChild, int dragLengthY) { - boolean canDragDown = hasActiveNotifications() - || mKeyguardMediaController.getView().getVisibility() == VISIBLE; + boolean canDragDown = hasActiveNotifications() || mKeyguardMediaControllorVisible; if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) { mLockscreenGestureLogger.write( MetricsEvent.ACTION_LS_SHADE, @@ -6655,7 +6574,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public boolean isDragDownAnywhereEnabled() { return mStatusbarStateController.getState() == StatusBarState.KEYGUARD - && !mKeyguardBypassController.getBypassEnabled(); + && !mKeyguardBypassEnabledProvider.getBypassEnabled(); } }; @@ -6838,4 +6757,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return INVALID; } } + + interface KeyguardBypassEnabledProvider { + boolean getBypassEnabled(); + } } 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 7c29ee2b5483..ca78b2a1fc68 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 @@ -18,8 +18,10 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; +import android.graphics.Point; import android.graphics.PointF; import android.provider.Settings; +import android.util.Log; import android.view.Display; import android.view.View; import android.view.ViewGroup; @@ -27,9 +29,20 @@ import android.view.WindowInsets; import android.widget.FrameLayout; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.media.KeyguardMediaController; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -42,6 +55,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; 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; @@ -49,6 +63,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import java.util.function.BiConsumer; @@ -56,11 +71,15 @@ import java.util.function.BiConsumer; import javax.inject.Inject; import javax.inject.Named; +import kotlin.Unit; + /** * Controller for {@link NotificationStackScrollLayout}. */ @StatusBarComponent.StatusBarScope public class NotificationStackScrollLayoutController { + private static final String TAG = "StackScrollerController"; + private final boolean mAllowLongPress; private final NotificationGutsManager mNotificationGutsManager; private final HeadsUpManagerPhone mHeadsUpManager; @@ -68,9 +87,18 @@ public class NotificationStackScrollLayoutController { private final TunerService mTunerService; private final DynamicPrivacyController mDynamicPrivacyController; private final ConfigurationController mConfigurationController; + private final ZenModeController mZenModeController; + private final MetricsLogger mMetricsLogger; + private final KeyguardMediaController mKeyguardMediaController; + private final SysuiStatusBarStateController mStatusBarStateController; + private final KeyguardBypassController mKeyguardBypassController; + private final SysuiColorExtractor mColorExtractor; + private final NotificationLockscreenUserManager mLockscreenUserManager; + + private NotificationStackScrollLayout mView; + private final NotificationListContainerImpl mNotificationListContainer = new NotificationListContainerImpl(); - private NotificationStackScrollLayout mView; @VisibleForTesting final View.OnAttachStateChangeListener mOnAttachStateChangeListener = @@ -78,11 +106,14 @@ public class NotificationStackScrollLayoutController { @Override public void onViewAttachedToWindow(View v) { mConfigurationController.addCallback(mConfigurationListener); + mStatusBarStateController.addCallback( + mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); } @Override public void onViewDetachedFromWindow(View v) { mConfigurationController.removeCallback(mConfigurationListener); + mStatusBarStateController.removeCallback(mStateListener); } }; @@ -122,6 +153,91 @@ public class NotificationStackScrollLayoutController { } }; + private final StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStatePreChange(int oldState, int newState) { + if (oldState == StatusBarState.SHADE_LOCKED + && newState == StatusBarState.KEYGUARD) { + mView.requestAnimateEverything(); + } + } + + @Override + public void onStateChanged(int newState) { + mView.setStatusBarState(newState); + } + + @Override + public void onStatePostChange() { + mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(), + mLockscreenUserManager.isAnyProfilePublicMode()); + mView.onStatePostChange(mStatusBarStateController.fromShadeLocked()); + } + }; + + private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() { + @Override + public void onUserChanged(int userId) { + mView.setCurrentUserid(userId); + mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode()); + } + }; + + private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { + @Override + public void onMenuClicked( + View view, int x, int y, NotificationMenuRowPlugin.MenuItem item) { + if (!mAllowLongPress) { + return; + } + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) + .setType(MetricsEvent.TYPE_ACTION) + ); + } + mNotificationGutsManager.openGuts(view, x, y, item); + } + + @Override + public void onMenuReset(View row) { + mView.onMenuReset(row); + } + + @Override + public void onMenuShown(View row) { + if (row instanceof ExpandableNotificationRow) { + ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; + mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) + .setType(MetricsEvent.TYPE_ACTION)); + mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); + mView.onMenuShown(row); + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, + false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + + // Check to see if we want to go directly to the notification guts + NotificationMenuRowPlugin provider = notificationRow.getProvider(); + if (provider.shouldShowGutsOnSnapOpen()) { + NotificationMenuRowPlugin.MenuItem item = provider.menuItemToExposeOnSnap(); + if (item != null) { + Point origin = provider.getRevealAnimationOrigin(); + mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); + } else { + Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " + + "menu item in menuItemtoExposeOnSnap. Skipping."); + } + + // Close the menu row since we went directly to the guts + mView.resetExposedMenuView(false, true); + } + } + } + }; + @Inject public NotificationStackScrollLayoutController( @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, @@ -130,7 +246,14 @@ public class NotificationStackScrollLayoutController { NotificationRoundnessManager notificationRoundnessManager, TunerService tunerService, DynamicPrivacyController dynamicPrivacyController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + SysuiStatusBarStateController statusBarStateController, + KeyguardMediaController keyguardMediaController, + KeyguardBypassController keyguardBypassController, + ZenModeController zenModeController, + SysuiColorExtractor colorExtractor, + NotificationLockscreenUserManager lockscreenUserManager, + MetricsLogger metricsLogger) { mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; @@ -138,19 +261,28 @@ public class NotificationStackScrollLayoutController { mTunerService = tunerService; mDynamicPrivacyController = dynamicPrivacyController; mConfigurationController = configurationController; + mStatusBarStateController = statusBarStateController; + mKeyguardMediaController = keyguardMediaController; + mKeyguardBypassController = keyguardBypassController; + mZenModeController = zenModeController; + mColorExtractor = colorExtractor; + mLockscreenUserManager = lockscreenUserManager; + mMetricsLogger = metricsLogger; } public void attach(NotificationStackScrollLayout view) { mView = view; mView.setController(this); - - if (mAllowLongPress) { - mView.setLongPressListener(mNotificationGutsManager::openGuts); - } + mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled); mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here? mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); + mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); + mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId()); + + mView.setMenuEventListener(mMenuEventListener); + mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); @@ -165,6 +297,23 @@ public class NotificationStackScrollLayoutController { Settings.Secure.NOTIFICATION_DISMISS_RTL, Settings.Secure.NOTIFICATION_HISTORY_ENABLED); + mColorExtractor.addOnColorsChangedListener((colorExtractor, which) -> { + final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); + mView.updateDecorViews(useDarkText); + }); + + mKeyguardMediaController.setVisibilityChangedListener(visible -> { + mView.setKeyguardMediaControllorVisible(visible); + if (visible) { + mView.generateAddAnimation( + mKeyguardMediaController.getView(), false /*fromMoreCard */); + } else { + mView.generateRemoveAnimation(mKeyguardMediaController.getView()); + } + mView.requestChildrenUpdate(); + return Unit.INSTANCE; + }); + if (mView.isAttachedToWindow()) { mOnAttachStateChangeListener.onViewAttachedToWindow(mView); } @@ -484,7 +633,7 @@ public class NotificationStackScrollLayoutController { } public void updateEmptyShadeView(boolean visible) { - mView.updateEmptyShadeView(visible); + mView.updateEmptyShadeView(visible, mZenModeController.areNotificationsHiddenInShade()); } public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { @@ -646,7 +795,7 @@ public class NotificationStackScrollLayoutController { @Override public void notifyGroupChildAdded(ExpandableView row) { - mView.onViewAddedInternal(row); + mView.notifyGroupChildAdded(row); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java index b7bc8c8fb7c4..302301d79c3a 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java @@ -24,7 +24,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.SystemServicesModule; import com.android.systemui.dagger.SystemUIBinder; import com.android.systemui.dagger.SystemUIModule; -import com.android.systemui.onehanded.dagger.OneHandedModule; import dagger.Subcomponent; @@ -36,7 +35,6 @@ import dagger.Subcomponent; DefaultComponentBinder.class, DependencyProvider.class, DependencyBinder.class, - OneHandedModule.class, SystemServicesModule.class, SystemUIBinder.class, SystemUIModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java index 3a2172ae0fae..66f8f74c7cab 100644 --- a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java +++ b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java @@ -25,13 +25,11 @@ import android.provider.Settings; import java.util.concurrent.Executor; -import javax.inject.Inject; - /** * Wrapper around DeviceConfig useful for testing. */ public class DeviceConfigProxy { - @Inject + public DeviceConfigProxy() { } diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt index f22f59bee42f..bcfb2afeeda1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt @@ -4,8 +4,7 @@ import android.graphics.Rect import android.util.Log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.FloatingContentCoordinator.FloatingContent -import java.util.* -import javax.inject.Inject +import java.util.HashMap /** Tag for debug logging. */ private const val TAG = "FloatingCoordinator" @@ -20,9 +19,9 @@ private const val TAG = "FloatingCoordinator" * other content out of the way. [onContentRemoved] should be called when the content is removed or * no longer visible. */ -@SysUISingleton -class FloatingContentCoordinator @Inject constructor() { +@SysUISingleton +class FloatingContentCoordinator constructor() { /** * Represents a piece of floating content, such as PIP or the Bubbles stack. Provides methods * that allow the [FloatingContentCoordinator] to determine the current location of the content, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index ac47660f3221..34398cc79bd2 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -20,8 +20,12 @@ import android.content.Context; import android.os.Handler; import android.view.IWindowManager; +import com.android.internal.logging.UiEventLogger; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.pip.PipUiEventLogger; +import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.FloatingContentCoordinator; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; @@ -51,8 +55,26 @@ public class WMShellBaseModule { @SysUISingleton @Provides + static DeviceConfigProxy provideDeviceConfigProxy() { + return new DeviceConfigProxy(); + } + @SysUISingleton + @Provides + static FloatingContentCoordinator provideFloatingContentCoordinator() { + return new FloatingContentCoordinator(); + } + + @SysUISingleton + @Provides + static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger) { + return new PipUiEventLogger(uiEventLogger); + } + + @SysUISingleton + @Provides static SystemWindows provideSystemWindows(DisplayController displayController, IWindowManager wmService) { return new SystemWindows(displayController, wmService); } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java index d7dba5fdf8c8..02d587d90655 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedControllerTest.java @@ -46,9 +46,9 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class OneHandedManagerImplTest extends OneHandedTestCase { +public class OneHandedControllerTest extends OneHandedTestCase { Display mDisplay; - OneHandedManagerImpl mOneHandedManagerImpl; + OneHandedController mOneHandedController; OneHandedTimeoutHandler mTimeoutHandler; @Mock @@ -70,7 +70,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mDisplay = mContext.getDisplay(); - mOneHandedManagerImpl = new OneHandedManagerImpl( + mOneHandedController = new OneHandedController( getContext(), mCommandQueue, mMockDisplayController, @@ -102,7 +102,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testStartOneHanded() { - mOneHandedManagerImpl.startOneHanded(); + mOneHandedController.startOneHanded(); verify(mMockDisplayAreaOrganizer).scheduleOffset(anyInt(), anyInt()); } @@ -110,7 +110,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testStopOneHanded() { when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false); - mOneHandedManagerImpl.stopOneHanded(); + mOneHandedController.stopOneHanded(); verify(mMockDisplayAreaOrganizer, never()).scheduleOffset(anyInt(), anyInt()); } @@ -122,7 +122,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testStopOneHanded_shouldRemoveTimer() { - mOneHandedManagerImpl.stopOneHanded(); + mOneHandedController.stopOneHanded(); verify(mTimeoutHandler).removeTimer(); } @@ -130,7 +130,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testUpdateIsEnabled() { final boolean enabled = true; - mOneHandedManagerImpl.setOneHandedEnabled(enabled); + mOneHandedController.setOneHandedEnabled(enabled); verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled); } @@ -138,7 +138,7 @@ public class OneHandedManagerImplTest extends OneHandedTestCase { @Test public void testUpdateSwipeToNotificationIsEnabled() { final boolean enabled = true; - mOneHandedManagerImpl.setSwipeToNotificationEnabled(enabled); + mOneHandedController.setSwipeToNotificationEnabled(enabled); verify(mMockTouchHandler, times(2)).onOneHandedEnabled(enabled); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java index 95a230f6511c..756382a6c630 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java @@ -30,8 +30,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -48,7 +48,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; - OneHandedManagerImpl mOneHandedManagerImpl; + OneHandedController mOneHandedController; @Mock CommandQueue mCommandQueue; @Mock @@ -66,7 +66,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { mTutorialHandler = new OneHandedTutorialHandler(mContext); mGestureHandler = Mockito.spy(new OneHandedGestureHandler( mContext, mMockDisplayController, mMockNavigationModeController)); - mOneHandedManagerImpl = new OneHandedManagerImpl( + mOneHandedController = new OneHandedController( getContext(), mCommandQueue, mMockDisplayController, @@ -93,15 +93,15 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { public void testReceiveNewConfig_whenSetOneHandedEnabled() { // 1st called at init verify(mGestureHandler).onOneHandedEnabled(true); - mOneHandedManagerImpl.setOneHandedEnabled(true); + mOneHandedController.setOneHandedEnabled(true); // 2nd called by setOneHandedEnabled() verify(mGestureHandler, times(2)).onOneHandedEnabled(true); } @Test public void testOneHandedDisabled_shouldDisposeInputChannel() { - mOneHandedManagerImpl.setOneHandedEnabled(false); - mOneHandedManagerImpl.setSwipeToNotificationEnabled(false); + mOneHandedController.setOneHandedEnabled(false); + mOneHandedController.setSwipeToNotificationEnabled(false); assertThat(mGestureHandler.mInputMonitor).isNull(); assertThat(mGestureHandler.mInputEventReceiver).isNull(); @@ -111,7 +111,7 @@ public class OneHandedGestureHandlerTest extends OneHandedTestCase { public void testChangeNavBarTo2Button_shouldDisposeInputChannel() { // 1st called at init verify(mGestureHandler).onOneHandedEnabled(true); - mOneHandedManagerImpl.setOneHandedEnabled(true); + mOneHandedController.setOneHandedEnabled(true); // 2nd called by setOneHandedEnabled() verify(mGestureHandler, times(2)).onOneHandedEnabled(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java index 8ae632dd5a47..3c3ace052e47 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java @@ -28,8 +28,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -46,7 +46,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; - OneHandedManagerImpl mOneHandedManagerImpl; + OneHandedController mOneHandedController; @Mock CommandQueue mCommandQueue; @Mock @@ -64,7 +64,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { mTouchHandler = Mockito.spy(new OneHandedTouchHandler()); mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, mMockNavigationModeController); - mOneHandedManagerImpl = new OneHandedManagerImpl( + mOneHandedController = new OneHandedController( getContext(), mCommandQueue, mMockDisplayController, @@ -88,14 +88,14 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { @Test public void testOneHandedDisabled_shouldDisposeInputChannel() { - mOneHandedManagerImpl.setOneHandedEnabled(false); + mOneHandedController.setOneHandedEnabled(false); assertThat(mTouchHandler.mInputMonitor).isNull(); assertThat(mTouchHandler.mInputEventReceiver).isNull(); } @Test public void testOneHandedEnabled_monitorInputChannel() { - mOneHandedManagerImpl.setOneHandedEnabled(true); + mOneHandedController.setOneHandedEnabled(true); assertThat(mTouchHandler.mInputMonitor).isNotNull(); assertThat(mTouchHandler.mInputEventReceiver).isNotNull(); } @@ -104,7 +104,7 @@ public class OneHandedTouchHandlerTest extends OneHandedTestCase { public void testReceiveNewConfig_whenSetOneHandedEnabled() { // 1st called at init verify(mTouchHandler).onOneHandedEnabled(true); - mOneHandedManagerImpl.setOneHandedEnabled(true); + mOneHandedController.setOneHandedEnabled(true); // 2nd called by setOneHandedEnabled() verify(mTouchHandler, times(2)).onOneHandedEnabled(true); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java index c75a8d2f5454..1bffbf7eb8dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java @@ -24,8 +24,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import com.android.systemui.model.SysUiState; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.statusbar.CommandQueue; import com.android.wm.shell.common.DisplayController; import org.junit.Before; @@ -42,7 +42,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { OneHandedTouchHandler mTouchHandler; OneHandedTutorialHandler mTutorialHandler; OneHandedGestureHandler mGestureHandler; - OneHandedManagerImpl mOneHandedManagerImpl; + OneHandedController mOneHandedController; @Mock CommandQueue mCommandQueue; @Mock @@ -61,7 +61,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase { mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext)); mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController, mMockNavigationModeController); - mOneHandedManagerImpl = new OneHandedManagerImpl( + mOneHandedController = new OneHandedController( getContext(), mCommandQueue, mMockDisplayController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java index f0e713e42046..ae3df5db30bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java @@ -48,7 +48,7 @@ public class OneHandedUITest extends OneHandedTestCase { OneHandedUI mOneHandedUI; ScreenLifecycle mScreenLifecycle; @Mock - OneHandedManagerImpl mMockOneHandedManagerImpl; + OneHandedController mOneHandedController; @Mock OneHandedTimeoutHandler mMockTimeoutHandler; @@ -59,7 +59,7 @@ public class OneHandedUITest extends OneHandedTestCase { mScreenLifecycle = new ScreenLifecycle(); mOneHandedUI = new OneHandedUI(mContext, mCommandQueue, - mMockOneHandedManagerImpl, + mOneHandedController, mScreenLifecycle); mOneHandedUI.start(); mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class); @@ -74,14 +74,14 @@ public class OneHandedUITest extends OneHandedTestCase { public void testStartOneHanded() { mOneHandedUI.startOneHanded(); - verify(mMockOneHandedManagerImpl).startOneHanded(); + verify(mOneHandedController).startOneHanded(); } @Test public void testStopOneHanded() { mOneHandedUI.stopOneHanded(); - verify(mMockOneHandedManagerImpl).stopOneHanded(); + verify(mOneHandedController).stopOneHanded(); } @Test @@ -89,7 +89,7 @@ public class OneHandedUITest extends OneHandedTestCase { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.TAPS_APP_TO_EXIT, 1); - verify(mMockOneHandedManagerImpl).setTaskChangeToExit(true); + verify(mOneHandedController).setTaskChangeToExit(true); } @Test @@ -97,7 +97,7 @@ public class OneHandedUITest extends OneHandedTestCase { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.ONE_HANDED_MODE_ENABLED, 1); - verify(mMockOneHandedManagerImpl).setOneHandedEnabled(true); + verify(mOneHandedController).setOneHandedEnabled(true); } @Test @@ -115,7 +115,7 @@ public class OneHandedUITest extends OneHandedTestCase { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1); - verify(mMockOneHandedManagerImpl).setSwipeToNotificationEnabled(true); + verify(mOneHandedController).setSwipeToNotificationEnabled(true); } @Ignore("Clarifying do not receive callback") @@ -123,14 +123,14 @@ public class OneHandedUITest extends OneHandedTestCase { public void testKeyguardBouncerShowing_shouldStopOneHanded() { mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true); - verify(mMockOneHandedManagerImpl).stopOneHanded(); + verify(mOneHandedController).stopOneHanded(); } @Test public void testScreenTurningOff_shouldStopOneHanded() { mScreenLifecycle.dispatchScreenTurningOff(); - verify(mMockOneHandedManagerImpl).stopOneHanded(); + verify(mOneHandedController).stopOneHanded(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java index e9d2b73182e0..cdb177096f11 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipBoundsHandlerTest.java @@ -19,7 +19,6 @@ package com.android.systemui.pip; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; import android.content.ComponentName; import android.graphics.Rect; @@ -33,7 +32,6 @@ import android.view.Gravity; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.wm.shell.common.DisplayController; import org.junit.Before; import org.junit.Test; @@ -65,8 +63,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase { @Before public void setUp() throws Exception { initializeMockResources(); - mPipBoundsHandler = new PipBoundsHandler(mContext, new PipSnapAlgorithm(mContext), - mock(DisplayController.class)); + mPipBoundsHandler = new PipBoundsHandler(mContext); mTestComponentName1 = new ComponentName(mContext, "component1"); mTestComponentName2 = new ComponentName(mContext, "component2"); @@ -113,7 +110,7 @@ public class PipBoundsHandlerTest extends SysuiTestCase { res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, newDefaultAspectRatio); - mPipBoundsHandler.onConfigurationChanged(); + mPipBoundsHandler.onConfigurationChanged(mContext); assertEquals("Default aspect ratio should be reloaded", mPipBoundsHandler.getDefaultAspectRatio(), newDefaultAspectRatio, diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java index 9f67722041aa..c8d4aca90519 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java @@ -72,9 +72,6 @@ public class PipTouchHandlerTest extends SysuiTestCase { private InputConsumerController mInputConsumerController; @Mock - private PipBoundsHandler mPipBoundsHandler; - - @Mock private PipTaskOrganizer mPipTaskOrganizer; @Mock @@ -89,6 +86,7 @@ public class PipTouchHandlerTest extends SysuiTestCase { @Mock private PipUiEventLogger mPipUiEventLogger; + private PipBoundsHandler mPipBoundsHandler; private PipSnapAlgorithm mPipSnapAlgorithm; private PipMotionHelper mMotionHelper; private PipResizeGestureHandler mPipResizeGestureHandler; @@ -104,11 +102,13 @@ public class PipTouchHandlerTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mPipBoundsHandler = new PipBoundsHandler(mContext); + mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm(); mPipSnapAlgorithm = new PipSnapAlgorithm(mContext); mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager, mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler, - mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, - mPipSnapAlgorithm, mSysUiState, mPipUiEventLogger); + mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, mSysUiState, + mPipUiEventLogger); mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper()); mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler()); mPipTouchHandler.setPipMotionHelper(mMotionHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java index 1523653dec3c..3dc29a1ae4d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java @@ -53,7 +53,6 @@ public class NotificationEntryBuilder { /* ListEntry properties */ private GroupEntry mParent; - private int mSection = -1; /* If set, use this creation time instead of mClock.uptimeMillis */ private long mCreationTime = -1; @@ -68,7 +67,6 @@ public class NotificationEntryBuilder { mRankingBuilder = new RankingBuilder(source.getRanking()); mParent = source.getParent(); - mSection = source.getSection(); mCreationTime = source.getCreationTime(); } @@ -104,7 +102,6 @@ public class NotificationEntryBuilder { /* ListEntry properties */ entry.setParent(mParent); - entry.getAttachState().setSectionIndex(mSection); return entry; } @@ -117,14 +114,6 @@ public class NotificationEntryBuilder { } /** - * Sets the section. - */ - public NotificationEntryBuilder setSection(int section) { - mSection = section; - return this; - } - - /** * Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be * ignored. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index 6fa5055c875d..8acb705c744d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -35,6 +35,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static java.util.Collections.singletonList; + import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -46,6 +48,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationInteractionTracker; import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; @@ -54,7 +57,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeL import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; 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.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; import com.android.systemui.util.time.FakeSystemClock; @@ -71,7 +74,6 @@ import org.mockito.Spy; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -496,7 +498,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { assertTrue(entry.hasFinishedInitialization()); // WHEN the pipeline is kicked off - mReadyForBuildListener.onBuildList(Arrays.asList(entry)); + mReadyForBuildListener.onBuildList(singletonList(entry)); // THEN the entry's initialization time is reset assertFalse(entry.hasFinishedInitialization()); @@ -609,13 +611,18 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a filter that removes all PACKAGE_4 notifs and sections that divide // notifs based on package name mListBuilder.addPreGroupFilter(new PackageFilter(PACKAGE_4)); - final NotifSection pkg1Section = spy(new PackageSection(PACKAGE_1)); - final NotifSection pkg2Section = spy(new PackageSection(PACKAGE_2)); + final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1)); + final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2)); // NOTE: no package 3 section explicitly added, so notifs with package 3 will get set by // ShadeListBuilder's sDefaultSection which will demote it to the last section - final NotifSection pkg4Section = spy(new PackageSection(PACKAGE_4)); - final NotifSection pkg5Section = spy(new PackageSection(PACKAGE_5)); - mListBuilder.setSections(Arrays.asList(pkg1Section, pkg2Section, pkg4Section, pkg5Section)); + final NotifSectioner pkg4Sectioner = spy(new PackageSectioner(PACKAGE_4)); + final NotifSectioner pkg5Sectioner = spy(new PackageSectioner(PACKAGE_5)); + mListBuilder.setSectioners( + Arrays.asList(pkg1Sectioner, pkg2Sectioner, pkg4Sectioner, pkg5Sectioner)); + + final NotifSection pkg1Section = new NotifSection(pkg1Sectioner, 0); + final NotifSection pkg2Section = new NotifSection(pkg2Sectioner, 1); + final NotifSection pkg5Section = new NotifSection(pkg5Sectioner, 3); // WHEN we build a list with different packages addNotif(0, PACKAGE_4); @@ -648,72 +655,61 @@ public class ShadeListBuilderTest extends SysuiTestCase { // THEN the first section (pkg1Section) is called on all top level elements (but // no children and no entries that were filtered out) - verify(pkg1Section).isInSection(mEntrySet.get(1)); - verify(pkg1Section).isInSection(mEntrySet.get(2)); - verify(pkg1Section).isInSection(mEntrySet.get(3)); - verify(pkg1Section).isInSection(mEntrySet.get(7)); - verify(pkg1Section).isInSection(mEntrySet.get(8)); - verify(pkg1Section).isInSection(mEntrySet.get(9)); - verify(pkg1Section).isInSection(mBuiltList.get(3)); - - verify(pkg1Section, never()).isInSection(mEntrySet.get(0)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(4)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(5)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(6)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(10)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(1)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(2)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(3)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(7)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(8)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(9)); + verify(pkg1Sectioner).isInSection(mBuiltList.get(3)); + + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(0)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(4)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(5)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(6)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(10)); // THEN the last section (pkg5Section) is not called on any of the entries that were // filtered or already in a section - verify(pkg5Section, never()).isInSection(mEntrySet.get(0)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(1)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(2)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(4)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(5)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(6)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(7)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(8)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(10)); - - verify(pkg5Section).isInSection(mEntrySet.get(3)); - verify(pkg5Section).isInSection(mEntrySet.get(9)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(0)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(1)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(2)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(4)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(5)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(6)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(7)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(8)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(10)); + + verify(pkg5Sectioner).isInSection(mEntrySet.get(3)); + verify(pkg5Sectioner).isInSection(mEntrySet.get(9)); // THEN the correct section is assigned for entries in pkg1Section - assertEquals(pkg1Section, mEntrySet.get(2).getNotifSection()); - assertEquals(0, mEntrySet.get(2).getSection()); - assertEquals(pkg1Section, mEntrySet.get(7).getNotifSection()); - assertEquals(0, mEntrySet.get(7).getSection()); + assertEquals(pkg1Section, mEntrySet.get(2).getSection()); + assertEquals(pkg1Section, mEntrySet.get(7).getSection()); // THEN the correct section is assigned for entries in pkg2Section - assertEquals(pkg2Section, mEntrySet.get(1).getNotifSection()); - assertEquals(1, mEntrySet.get(1).getSection()); - assertEquals(pkg2Section, mEntrySet.get(8).getNotifSection()); - assertEquals(1, mEntrySet.get(8).getSection()); - assertEquals(pkg2Section, mBuiltList.get(3).getNotifSection()); - assertEquals(1, mBuiltList.get(3).getSection()); + assertEquals(pkg2Section, mEntrySet.get(1).getSection()); + assertEquals(pkg2Section, mEntrySet.get(8).getSection()); + assertEquals(pkg2Section, mBuiltList.get(3).getSection()); // THEN no section was assigned to entries in pkg4Section (since they were filtered) - assertEquals(null, mEntrySet.get(0).getNotifSection()); - assertEquals(-1, mEntrySet.get(0).getSection()); - assertEquals(null, mEntrySet.get(10).getNotifSection()); - assertEquals(-1, mEntrySet.get(10).getSection()); - + assertNull(mEntrySet.get(0).getSection()); + assertNull(mEntrySet.get(10).getSection()); // THEN the correct section is assigned for entries in pkg5Section - assertEquals(pkg5Section, mEntrySet.get(9).getNotifSection()); - assertEquals(3, mEntrySet.get(9).getSection()); + assertEquals(pkg5Section, mEntrySet.get(9).getSection()); // THEN the children entries are assigned the same section as its parent - assertEquals(mBuiltList.get(3).getNotifSection(), child(5).entry.getNotifSection()); assertEquals(mBuiltList.get(3).getSection(), child(5).entry.getSection()); - assertEquals(mBuiltList.get(3).getNotifSection(), child(6).entry.getNotifSection()); assertEquals(mBuiltList.get(3).getSection(), child(6).entry.getSection()); } @Test public void testNotifUsesDefaultSection() { // GIVEN a Section for Package2 - final NotifSection pkg2Section = spy(new PackageSection(PACKAGE_2)); - mListBuilder.setSections(Arrays.asList(pkg2Section)); + final NotifSectioner pkg2Section = spy(new PackageSectioner(PACKAGE_2)); + mListBuilder.setSectioners(singletonList(pkg2Section)); // WHEN we build a list with pkg1 and pkg2 packages addNotif(0, PACKAGE_1); @@ -727,8 +723,8 @@ public class ShadeListBuilderTest extends SysuiTestCase { ); // THEN the entry that didn't have an explicit section gets assigned the DefaultSection - assertEquals(1, notif(0).entry.getSection()); - assertNotNull(notif(0).entry.getNotifSection()); + assertNotNull(notif(0).entry.getSection()); + assertEquals(1, notif(0).entry.getSectionIndex()); } @Test @@ -763,15 +759,15 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a bunch of registered listeners and pluggables NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_1)); NotifPromoter promoter = spy(new IdPromoter(3)); - NotifSection section = spy(new PackageSection(PACKAGE_1)); + NotifSectioner section = spy(new PackageSectioner(PACKAGE_1)); NotifComparator comparator = spy(new HypeComparator(PACKAGE_4)); NotifFilter preRenderFilter = spy(new PackageFilter(PACKAGE_5)); mListBuilder.addPreGroupFilter(preGroupFilter); mListBuilder.addOnBeforeTransformGroupsListener(mOnBeforeTransformGroupsListener); mListBuilder.addPromoter(promoter); mListBuilder.addOnBeforeSortListener(mOnBeforeSortListener); - mListBuilder.setComparators(Collections.singletonList(comparator)); - mListBuilder.setSections(Arrays.asList(section)); + mListBuilder.setComparators(singletonList(comparator)); + mListBuilder.setSectioners(singletonList(section)); mListBuilder.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener); mListBuilder.addFinalizeFilter(preRenderFilter); mListBuilder.addOnBeforeRenderListListener(mOnBeforeRenderListListener); @@ -821,13 +817,13 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a variety of pluggables NotifFilter packageFilter = new PackageFilter(PACKAGE_1); NotifPromoter idPromoter = new IdPromoter(4); - NotifSection section = new PackageSection(PACKAGE_1); + NotifSectioner section = new PackageSectioner(PACKAGE_1); NotifComparator hypeComparator = new HypeComparator(PACKAGE_2); mListBuilder.addPreGroupFilter(packageFilter); mListBuilder.addPromoter(idPromoter); - mListBuilder.setSections(Arrays.asList(section)); - mListBuilder.setComparators(Collections.singletonList(hypeComparator)); + mListBuilder.setSectioners(singletonList(section)); + mListBuilder.setComparators(singletonList(hypeComparator)); // GIVEN a set of random notifs addNotif(0, PACKAGE_1); @@ -973,7 +969,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { RecordingOnBeforeSortListener listener = new RecordingOnBeforeSortListener(); mListBuilder.addOnBeforeSortListener(listener); - mListBuilder.setComparators(Arrays.asList(new HypeComparator(PACKAGE_3))); + mListBuilder.setComparators(singletonList(new HypeComparator(PACKAGE_3))); // GIVEN some new notifs out of order addNotif(0, PACKAGE_1); @@ -1093,7 +1089,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { NotifComparator comparator = new HypeComparator(PACKAGE_5); OnBeforeRenderListListener listener = (list) -> comparator.invalidateList(); - mListBuilder.setComparators(Collections.singletonList(comparator)); + mListBuilder.setComparators(singletonList(comparator)); mListBuilder.addOnBeforeRenderListListener(listener); // WHEN we try to run the pipeline and the comparator is invalidated @@ -1420,10 +1416,10 @@ public class ShadeListBuilderTest extends SysuiTestCase { } /** Represents a section for the passed pkg */ - private static class PackageSection extends NotifSection { + private static class PackageSectioner extends NotifSectioner { private final String mPackage; - PackageSection(String pkg) { + PackageSectioner(String pkg) { super("PackageSection_" + pkg); mPackage = pkg; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java index ce8ce2e39bcc..639e791cbf23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java @@ -21,11 +21,9 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_MIN; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,7 +35,6 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.ArraySet; import androidx.test.filters.SmallTest; @@ -48,8 +45,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -61,8 +57,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -78,7 +72,7 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { private AppOpsCoordinator mAppOpsCoordinator; private NotifFilter mForegroundFilter; private NotifLifetimeExtender mForegroundNotifLifetimeExtender; - private NotifSection mFgsSection; + private NotifSectioner mFgsSection; private FakeSystemClock mClock = new FakeSystemClock(); private FakeExecutor mExecutor = new FakeExecutor(mClock); @@ -111,7 +105,7 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { lifetimeExtenderCaptor.capture()); mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue(); - mFgsSection = mAppOpsCoordinator.getSection(); + mFgsSection = mAppOpsCoordinator.getSectioner(); } @Test 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 be5c8a846afb..c49393d2ed34 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 @@ -25,7 +25,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON import org.junit.Assert.assertFalse @@ -45,7 +45,7 @@ import org.mockito.Mockito.`when` as whenever class ConversationCoordinatorTest : SysuiTestCase() { // captured listeners and pluggables: private lateinit var promoter: NotifPromoter - private lateinit var peopleSection: NotifSection + private lateinit var peopleSectioner: NotifSectioner @Mock private lateinit var pipeline: NotifPipeline @@ -70,7 +70,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { verify(pipeline).addPromoter(notifPromoterCaptor.capture()) promoter = notifPromoterCaptor.value - peopleSection = coordinator.getSection() + peopleSectioner = coordinator.sectioner entry = NotificationEntryBuilder().setChannel(channel).build() } @@ -88,7 +88,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { entry.sbn, entry.ranking)).thenReturn(TYPE_PERSON) // only put people notifications in this section - assertTrue(peopleSection.isInSection(entry)) - assertFalse(peopleSection.isInSection(NotificationEntryBuilder().build())) + assertTrue(peopleSectioner.isInSection(entry)) + assertFalse(peopleSectioner.isInSection(NotificationEntryBuilder().build())) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java index 730481afe638..fa992a5d5dbb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder; @@ -64,7 +64,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { private NotifPromoter mNotifPromoter; private NotifLifetimeExtender mNotifLifetimeExtender; private OnHeadsUpChangedListener mOnHeadsUpChangedListener; - private NotifSection mNotifSection; + private NotifSectioner mNotifSectioner; @Mock private NotifPipeline mNotifPipeline; @Mock private HeadsUpManager mHeadsUpManager; @@ -111,7 +111,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue(); mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue(); - mNotifSection = mCoordinator.getSection(); + mNotifSectioner = mCoordinator.getSectioner(); mNotifLifetimeExtender.setCallback(mEndLifetimeExtension); mEntry = new NotificationEntryBuilder().build(); } @@ -132,8 +132,8 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { setCurrentHUN(mEntry); // THEN only section the current HUN, mEntry - assertTrue(mNotifSection.isInSection(mEntry)); - assertFalse(mNotifSection.isInSection(new NotificationEntryBuilder().build())); + assertTrue(mNotifSectioner.isInSection(mEntry)); + assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder().build())); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java index 5f10f38b2ee8..3a7d28ab56ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import org.junit.Before; @@ -61,8 +61,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { private NotifFilter mCapturedSuspendedFilter; private NotifFilter mCapturedDozingFilter; - private NotifSection mAlertingSection; - private NotifSection mSilentSection; + private NotifSectioner mAlertingSectioner; + private NotifSectioner mSilentSectioner; @Before public void setup() { @@ -76,8 +76,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0); mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1); - mAlertingSection = rankingCoordinator.getAlertingSection(); - mSilentSection = rankingCoordinator.getSilentSection(); + mAlertingSectioner = rankingCoordinator.getAlertingSectioner(); + mSilentSectioner = rankingCoordinator.getSilentSectioner(); } @Test @@ -146,8 +146,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true); // THEN entry is in the alerting section - assertTrue(mAlertingSection.isInSection(mEntry)); - assertFalse(mSilentSection.isInSection(mEntry)); + assertTrue(mAlertingSectioner.isInSection(mEntry)); + assertFalse(mSilentSectioner.isInSection(mEntry)); } @Test @@ -156,8 +156,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); // THEN entry is in the silent section - assertFalse(mAlertingSection.isInSection(mEntry)); - assertTrue(mSilentSection.isInSection(mEntry)); + assertFalse(mAlertingSectioner.isInSection(mEntry)); + assertTrue(mSilentSectioner.isInSection(mEntry)); } private RankingBuilder getRankingForUnfilteredNotif() { 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 9a6674e165e4..bca7b312ff15 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 @@ -24,9 +24,7 @@ import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; @@ -47,19 +45,15 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.testing.UiEventLoggerFake; 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.media.KeyguardMediaController; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -87,14 +81,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider; 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.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.leak.LeakDetector; import org.junit.After; @@ -103,7 +96,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -135,14 +127,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private MetricsLogger mMetricsLogger; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; - @Mock private KeyguardBypassController mKeyguardBypassController; - @Mock private KeyguardMediaController mKeyguardMediaController; - @Mock private ZenModeController mZenModeController; + @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; @Mock private NotificationSectionsManager mNotificationSectionsManager; @Mock private NotificationSection mNotificationSection; - @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private FeatureFlags mFeatureFlags; - private UserChangedListener mUserChangedListener; + @Mock private SysuiStatusBarStateController mStatusBarStateController; private NotificationEntryManager mEntryManager; private int mOriginalInterruptionModelSetting; private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake(); @@ -172,8 +161,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mDependency.injectMockDependency(ShadeController.class); when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); - ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor - .forClass(UserChangedListener.class); mEntryManager = new NotificationEntryManager( mock(NotificationEntryManagerLogger.class), mock(NotificationGroupManager.class), @@ -211,17 +198,15 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // which refer to members of NotificationStackScrollLayout. The spy // holds a copy of the CUT's instances of these KeyguardBypassController, so they still // refer to the CUT's member variables, not the spy's member variables. - mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null, + mStackScrollerInternal = new NotificationStackScrollLayout( + getContext(), + null, mNotificationRoundnessManager, mock(DynamicPrivacyController.class), - mock(SysuiStatusBarStateController.class), + mStatusBarStateController, mHeadsUpManager, - mKeyguardBypassController, - mKeyguardMediaController, new FalsingManagerFake(), - mLockscreenUserManager, mock(NotificationGutsManager.class), - mZenModeController, mNotificationSectionsManager, mock(ForegroundServiceSectionController.class), mock(ForegroundServiceDismissalFeatureController.class), @@ -231,8 +216,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mock(NotifCollection.class), mUiEventLoggerFake ); - verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture()); - mUserChangedListener = userChangedCaptor.getValue(); + mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider); mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelfController(notificationShelfController); mStackScroller.setStatusBar(mBar); @@ -269,9 +253,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void updateEmptyView_dndSuppressing() { when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, true); verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text); } @@ -280,9 +263,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void updateEmptyView_dndNotSuppressing() { mStackScroller.setEmptyShadeView(mEmptyShadeView); when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, false); verify(mEmptyShadeView).setText(R.string.empty_shade_text); } @@ -291,12 +273,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void updateEmptyView_noNotificationsToDndSuppressing() { mStackScroller.setEmptyShadeView(mEmptyShadeView); when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, false); verify(mEmptyShadeView).setText(R.string.empty_shade_text); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, true); verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text); } @@ -312,12 +292,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void testOnStatePostChange_verifyIfProfileIsPublic() { - mUserChangedListener.onUserChanged(0); - verify(mLockscreenUserManager).isAnyProfilePublicMode(); - } - - @Test public void manageNotifications_visible() { FooterView view = mock(FooterView.class); mStackScroller.setFooterView(view); @@ -473,61 +447,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertNull(swipeActionHelper.getExposedMenuView()); } - class LogMatcher implements ArgumentMatcher<LogMaker> { - private int mCategory, mType; - - LogMatcher(int category, int type) { - mCategory = category; - mType = type; - } - public boolean matches(LogMaker l) { - return (l.getCategory() == mCategory) - && (l.getType() == mType); - } - - public String toString() { - return String.format("LogMaker(%d, %d)", mCategory, mType); - } - } - - private LogMaker logMatcher(int category, int type) { - return argThat(new LogMatcher(category, type)); - } - - @Test - @UiThreadTest - public void testOnMenuClickedLogging() { - // Set up the object under test to have a valid mLongPressListener. We're testing an - // anonymous-class member, mMenuEventListener, so we need to modify the state of the - // class itself, not the Mockito spy copied from it. See notes in setup. - mStackScrollerInternal.setLongPressListener( - mock(ExpandableNotificationRow.LongPressListener.class)); - - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); - when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( - MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - - mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock( - NotificationMenuRowPlugin.MenuItem.class)); - verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data - verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, - MetricsProto.MetricsEvent.TYPE_ACTION)); - } - - @Test - @UiThreadTest - public void testOnMenuShownLogging() { ; - - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); - when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( - MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - - mStackScroller.mMenuEventListener.onMenuShown(row); - verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data - verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, - MetricsProto.MetricsEvent.TYPE_ACTION)); - } - @Test public void testClearNotifications_All() { mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_ALL, true); 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 e3acf0213725..83d6ac904bfe 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 @@ -17,25 +17,45 @@ package com.android.systemui.statusbar.notification.stack; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.metrics.LogMaker; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.SysuiTestCase; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.media.KeyguardMediaController; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +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.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.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -55,15 +75,27 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { @Mock private TunerService mTunerService; @Mock - private AmbientState mAmbientState; - @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock private ConfigurationController mConfigurationController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; + @Mock + private ZenModeController mZenModeController; + @Mock + private KeyguardMediaController mKeyguardMediaController; + @Mock + private SysuiStatusBarStateController mSysuiStatusBarStateController; + @Mock + private KeyguardBypassController mKeyguardBypassController; + @Mock + private SysuiColorExtractor mColorExtractor; + @Mock + private NotificationLockscreenUserManager mNotificationLockscreenUserManager; + @Mock + private MetricsLogger mMetricsLogger; - NotificationStackScrollLayoutController mController; + private NotificationStackScrollLayoutController mController; @Before public void setUp() { @@ -76,7 +108,14 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mNotificationRoundnessManager, mTunerService, mDynamicPrivacyController, - mConfigurationController + mConfigurationController, + mSysuiStatusBarStateController, + mKeyguardMediaController, + mKeyguardBypassController, + mZenModeController, + mColorExtractor, + mNotificationLockscreenUserManager, + mMetricsLogger ); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); @@ -112,4 +151,140 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mController.mConfigurationListener.onDensityOrFontScaleChanged(); verify(mNotificationStackScrollLayout).reinflateViews(); } + + @Test + public void testUpdateEmptyShadeView_notificationsVisible() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); + mController.attach(mNotificationStackScrollLayout); + + mController.updateEmptyShadeView(true /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + true /* visible */, + true /* notifVisibleInShade */); + reset(mNotificationStackScrollLayout); + mController.updateEmptyShadeView(false /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + false /* visible */, + true /* notifVisibleInShade */); + } + + @Test + public void testUpdateEmptyShadeView_notificationsHidden() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); + mController.attach(mNotificationStackScrollLayout); + + mController.updateEmptyShadeView(true /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + true /* visible */, + false /* notifVisibleInShade */); + reset(mNotificationStackScrollLayout); + mController.updateEmptyShadeView(false /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + false /* visible */, + false /* notifVisibleInShade */); + } + + @Test + public void testOnUserChange_verifySensitiveProfile() { + when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true); + + ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor + .forClass(UserChangedListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationLockscreenUserManager) + .addUserChangedListener(userChangedCaptor.capture()); + reset(mNotificationStackScrollLayout); + + UserChangedListener changedListener = userChangedCaptor.getValue(); + changedListener.onUserChanged(0); + verify(mNotificationStackScrollLayout).setCurrentUserid(0); + verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); + } + + @Test + 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()); + + StatusBarStateController.StateListener stateListener = + stateListenerArgumentCaptor.getValue(); + + stateListener.onStatePostChange(); + verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); + } + + + @Test + public void testOnMenuShownLogging() { ; + + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + + ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor = + ArgumentCaptor.forClass(OnMenuEventListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).setMenuEventListener( + onMenuEventListenerArgumentCaptor.capture()); + + OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue(); + + onMenuEventListener.onMenuShown(row); + verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + @Test + public void testOnMenuClickedLogging() { + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + + ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor = + ArgumentCaptor.forClass(OnMenuEventListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).setMenuEventListener( + onMenuEventListenerArgumentCaptor.capture()); + + OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue(); + + onMenuEventListener.onMenuClicked(row, 0, 0, mock( + NotificationMenuRowPlugin.MenuItem.class)); + verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + private LogMaker logMatcher(int category, int type) { + return argThat(new LogMatcher(category, type)); + } + + static class LogMatcher implements ArgumentMatcher<LogMaker> { + private int mCategory, mType; + + LogMatcher(int category, int type) { + mCategory = category; + mType = type; + } + public boolean matches(LogMaker l) { + return (l.getCategory() == mCategory) + && (l.getType() == mType); + } + + public String toString() { + return String.format("LogMaker(%d, %d)", mCategory, mType); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java index 260ff2dafeed..33ece0084906 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java @@ -70,9 +70,7 @@ public class DeviceConfigProxyFake extends DeviceConfigProxy { for (Pair<Executor, OnPropertiesChangedListener> listener : mListeners) { Properties.Builder propBuilder = new Properties.Builder(namespace); - for (String key : mProperties.get(namespace).keySet()) { - propBuilder.setString(key, mProperties.get(namespace).get(key)); - } + propBuilder.setString(name, value); listener.first.execute(() -> listener.second.onPropertiesChanged(propBuilder.build())); } return true; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java new file mode 100644 index 000000000000..64e89579393e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java @@ -0,0 +1,100 @@ +/* + * 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; + +import static android.provider.DeviceConfig.Properties; + +import static com.google.common.truth.Truth.assertThat; + +import android.provider.DeviceConfig.OnPropertiesChangedListener; + +import androidx.annotation.NonNull; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DeviceConfigProxyFakeTest extends SysuiTestCase { + private static final String NAMESPACE = "foobar"; + + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + + private DeviceConfigProxyFake mDeviceConfigProxyFake; + + @Before + public void setup() { + mDeviceConfigProxyFake = new DeviceConfigProxyFake(); + } + + @Test + public void testOnPropertiesChanged() { + TestableListener onPropertiesChangedListener = new TestableListener(); + String key = "foo"; + String value = "bar"; + + mDeviceConfigProxyFake.addOnPropertiesChangedListener( + NAMESPACE, mFakeExecutor, onPropertiesChangedListener); + + mDeviceConfigProxyFake.setProperty(NAMESPACE, key, value, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(key, "")).isEqualTo(value); + } + + @Test + public void testOnMultiplePropertiesChanged() { + TestableListener onPropertiesChangedListener = new TestableListener(); + String keyA = "foo"; + String valueA = "bar"; + String keyB = "bada"; + String valueB = "boom"; + + mDeviceConfigProxyFake.addOnPropertiesChangedListener( + NAMESPACE, mFakeExecutor, onPropertiesChangedListener); + mDeviceConfigProxyFake.setProperty(NAMESPACE, keyA, valueA, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(keyA, "")).isEqualTo(valueA); + + mDeviceConfigProxyFake.setProperty(NAMESPACE, keyB, valueB, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(keyB, "")).isEqualTo(valueB); + } + + private static class TestableListener implements OnPropertiesChangedListener { + Properties mProperties; + + TestableListener() { + } + @Override + public void onPropertiesChanged(@NonNull Properties properties) { + mProperties = properties; + } + } +} diff --git a/services/Android.bp b/services/Android.bp index b348b91a1bd7..ef52c2aff002 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -156,10 +156,14 @@ droidstubs { java_library { name: "android_system_server_stubs_current", + defaults: ["android_stubs_dists_default"], srcs: [":services-stubs.sources"], installable: false, static_libs: ["android_module_lib_stubs_current"], sdk_version: "none", system_modules: "none", java_version: "1.8", + dist: { + dir: "apistubs/android/system-server", + }, } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 669bb24e0e77..a75b64ce08f5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -284,11 +284,13 @@ public class AccessibilityWindowManager { * Computes partial interactive region of given windowId. * * @param windowId The windowId + * @param forceComputeRegion set outRegion when the windowId matches one on the screen even + * though the region is not covered by other windows above it. * @param outRegion The output to which to write the bounds. - * @return true if outRegion is not empty. + * @return {@code true} if outRegion is not empty. */ boolean computePartialInteractiveRegionForWindowLocked(int windowId, - @NonNull Region outRegion) { + boolean forceComputeRegion, @NonNull Region outRegion) { if (mWindows == null) { return false; } @@ -309,6 +311,9 @@ public class AccessibilityWindowManager { currentWindow.getRegionInScreen(currentWindowRegions); outRegion.set(currentWindowRegions); windowInteractiveRegion = outRegion; + if (forceComputeRegion) { + windowInteractiveRegionChanged = true; + } continue; } } else if (currentWindow.getType() @@ -1240,10 +1245,13 @@ public class AccessibilityWindowManager { */ public boolean computePartialInteractiveRegionForWindowLocked(int windowId, @NonNull Region outRegion) { - windowId = resolveParentWindowIdLocked(windowId); - final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId); + final int parentWindowId = resolveParentWindowIdLocked(windowId); + final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked( + parentWindowId); + if (observer != null) { - return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion); + return observer.computePartialInteractiveRegionForWindowLocked(parentWindowId, + parentWindowId != windowId, outRegion); } return false; 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 a2d58c8019fc..b587dd33c4e5 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -561,24 +561,6 @@ public class TouchExplorer extends BaseEventStreamTransformation // stream consistent. sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } - if (mGestureDetector.isMultiFingerGesturesEnabled() - && mGestureDetector.isTwoFingerPassthroughEnabled()) { - if (event.getPointerCount() == 3) { - boolean isOnBottomEdge = false; - // If three fingers go down on the bottom edge of the screen, delegate immediately. - final long screenHeight = mContext.getResources().getDisplayMetrics().heightPixels; - for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { - if (mReceivedPointerTracker.getReceivedPointerDownY(i) - > (screenHeight - mEdgeSwipeHeightPixels)) { - isOnBottomEdge = true; - } - } - if (isOnBottomEdge) { - mState.startDelegating(); - mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); - } - } - } } /** @@ -644,12 +626,34 @@ public class TouchExplorer extends BaseEventStreamTransformation break; default: if (mGestureDetector.isMultiFingerGesturesEnabled()) { - return; + if (mGestureDetector.isTwoFingerPassthroughEnabled()) { + if (event.getPointerCount() == 3) { + boolean isOnBottomEdge = true; + // If three fingers went down on the bottom edge of the screen, delegate + // immediately. + final long screenHeight = + mContext.getResources().getDisplayMetrics().heightPixels; + for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { + if (mReceivedPointerTracker.getReceivedPointerDownY(i) + < (screenHeight - mEdgeSwipeHeightPixels)) { + isOnBottomEdge = false; + } + } + if (isOnBottomEdge) { + if (DEBUG) { + Slog.d(LOG_TAG, "Three-finger edge swipe detected."); + } + mState.startDelegating(); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); + } + } + } + } else { + // More than two pointers are delegated to the view hierarchy. + mState.startDelegating(); + event = MotionEvent.obtainNoHistory(event); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); } - // More than two pointers are delegated to the view hierarchy. - mState.startDelegating(); - event = MotionEvent.obtainNoHistory(event); - mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); break; } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 6d71c8e68f77..19871f993d42 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -315,11 +315,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>> mPreciseDataConnectionStates; - static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = - PhoneStateListener.LISTEN_REGISTRATION_FAILURE - | PhoneStateListener.LISTEN_BARRING_INFO; - - static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK = + // Starting in Q, almost all cellular location requires FINE location enforcement. + // Prior to Q, cellular was available with COARSE location enforcement. Bits in this + // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later. + static final int ENFORCE_LOCATION_PERMISSION_MASK = PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_CELL_INFO | PhoneStateListener.LISTEN_REGISTRATION_FAILURE @@ -376,7 +375,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + " newDefaultPhoneId=" + newDefaultPhoneId); } - //Due to possible risk condition,(notify call back using the new + //Due to possible race condition,(notify call back using the new //defaultSubId comes before new defaultSubId update) we need to recall all //possible missed notify callback synchronized (mRecords) { @@ -909,7 +908,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { try { if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { // null will be translated to empty CellLocation object in client. r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } @@ -964,7 +964,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -1518,7 +1519,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && idMatch(r.subId, subId, phoneId) && - checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { log("notifyCellInfoForSubscriber: mCellInfo=" + cellInfo @@ -1797,7 +1799,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId) && - checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { log("notifyCellLocation: cellLocation=" + cellLocation @@ -2544,16 +2547,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { boolean shouldCheckLocationPermissions = false; - if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) { + if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) { // Everything that requires fine location started in Q. So far... locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); - // If we're enforcing fine starting in Q, we also want to enforce coarse starting in Q. - locationQueryBuilder.setMinSdkVersionForCoarse(Build.VERSION_CODES.Q); - locationQueryBuilder.setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q); - shouldCheckLocationPermissions = true; - } - - if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) { + // If we're enforcing fine starting in Q, we also want to enforce coarse even for + // older SDK versions. locationQueryBuilder.setMinSdkVersionForCoarse(0); locationQueryBuilder.setMinSdkVersionForEnforcement(0); shouldCheckLocationPermissions = true; @@ -2750,8 +2748,16 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" + mServiceState[phoneId]); - r.callback.onServiceStateChanged( - new ServiceState(mServiceState[phoneId])); + ServiceState ss = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(ss); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged( + ss.createLocationInfoSanitizedCopy(false)); + } else { + r.callback.onServiceStateChanged( + ss.createLocationInfoSanitizedCopy(true)); + } } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2796,7 +2802,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " + mCellInfo.get(phoneId)); } - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -2862,7 +2869,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = " + mCellIdentity[phoneId]); } - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { // null will be translated to empty CellLocation object in client. r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 1815dac4c3a8..990a547144a0 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -99,6 +99,7 @@ public class Watchdog { "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", + "android.hardware.audio@7.0::IDevicesFactory", "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint", "android.hardware.bluetooth@1.0::IBluetoothHci", diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 041bedc3c575..ec12a971e445 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -210,6 +210,7 @@ public class SyncManager { private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; + private static final boolean USE_WTF_FOR_ACCOUNT_ERROR = false; private static final int SYNC_OP_STATE_VALID = 0; // "1" used to include errors 3, 4 and 5 but now it's split up. @@ -3446,7 +3447,7 @@ public class SyncManager { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: account doesn't exist."); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: account doesn't exist."); + logAccountError("SYNC_OP_STATE_INVALID: account doesn't exist."); return SYNC_OP_STATE_INVALID_NO_ACCOUNT; } // Drop this sync request if it isn't syncable. @@ -3456,14 +3457,14 @@ public class SyncManager { Slog.v(TAG, " Dropping sync operation: " + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS"); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS"); + logAccountError("SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS"); return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS; } if (state == AuthorityInfo.NOT_SYNCABLE) { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: isSyncable == NOT_SYNCABLE"); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: NOT_SYNCABLE"); + logAccountError("SYNC_OP_STATE_INVALID: NOT_SYNCABLE"); return SYNC_OP_STATE_INVALID_NOT_SYNCABLE; } @@ -3482,12 +3483,20 @@ public class SyncManager { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: disallowed by settings/network."); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: disallowed by settings/network"); + logAccountError("SYNC_OP_STATE_INVALID: disallowed by settings/network"); return SYNC_OP_STATE_INVALID_SYNC_DISABLED; } return SYNC_OP_STATE_VALID; } + private void logAccountError(String message) { + if (USE_WTF_FOR_ACCOUNT_ERROR) { + Slog.wtf(TAG, message); + } else { + Slog.e(TAG, message); + } + } + private boolean dispatchSyncOperation(SyncOperation op) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9c8b972985eb..5fba8b93ff4c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -367,7 +367,6 @@ import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; -import com.android.server.pm.permission.PermissionsState; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.security.VerityUtils; @@ -1818,7 +1817,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { removeMessages(WRITE_SETTINGS); removeMessages(WRITE_PACKAGE_RESTRICTIONS); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); mDirtyUsers.clear(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -1838,6 +1837,7 @@ public class PackageManagerService extends IPackageManager.Stub Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mLock) { removeMessages(WRITE_PACKAGE_LIST); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.writePackageListLPr(msg.arg1); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -2518,7 +2518,7 @@ public class PackageManagerService extends IPackageManager.Stub } mSettings.onVolumeForgotten(fsUuid); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } }; @@ -3442,6 +3442,7 @@ public class PackageManagerService extends IPackageManager.Stub + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); + mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some @@ -3561,7 +3562,7 @@ public class PackageManagerService extends IPackageManager.Stub // can downgrade to reader t.traceBegin("write settings"); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); t.traceEnd(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); @@ -3765,7 +3766,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); } mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } catch (PackageManagerException e) { // Whoops! Something went very wrong; roll back to the stub and disable the package @@ -3776,9 +3777,8 @@ public class PackageManagerService extends IPackageManager.Stub // If we don't, installing the system package fails during scan enableSystemPackageLPw(stubPkg); } - installPackageFromSystemLIF(stubPkg.getCodePath(), - null /*allUserHandles*/, null /*origUserHandles*/, - null /*origPermissionsState*/, true /*writeSettings*/); + installPackageFromSystemLIF(stubPkg.getCodePath(), null /*allUserHandles*/, + null /*origUserHandles*/, true /*writeSettings*/); } catch (PackageManagerException pme) { // Serious WTF; we have to be able to install the stub Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(), @@ -3792,7 +3792,7 @@ public class PackageManagerService extends IPackageManager.Stub stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } return false; @@ -13092,7 +13092,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } if (sendRemoved) { - killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId), + killApplication(packageName, pkgSetting.appId, userId, "hiding pkg"); sendApplicationHiddenForUser(packageName, pkgSetting, userId); return true; @@ -16280,7 +16280,7 @@ public class PackageManagerService extends IPackageManager.Stub res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); //to update install status Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings"); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -18877,6 +18877,10 @@ public class PackageManagerService extends IPackageManager.Stub if (outInfo != null) { outInfo.removedAppId = removedAppId; } + if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0) + && !isUpdatedSystemApp(deletedPs)) { + mPermissionManager.removePermissionsStateTEMP(removedAppId); + } mPermissionManager.updatePermissions(deletedPs.name, null); if (deletedPs.sharedUser != null) { // Remove permissions associated with package. Since runtime @@ -18886,10 +18890,10 @@ public class PackageManagerService extends IPackageManager.Stub // package is successful and this causes a change in gids. boolean shouldKill = false; for (int userId : UserManagerService.getInstance().getUserIds()) { - final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs, - userId); - shouldKill |= userIdToKill == UserHandle.USER_ALL - || userIdToKill >= UserHandle.USER_SYSTEM; + final int userIdToKill = mPermissionManager + .revokeSharedUserPermissionsForDeletedPackageTEMP(deletedPs, + userId); + shouldKill |= userIdToKill != UserHandle.USER_NULL; } // If gids changed, kill all affected packages. if (shouldKill) { @@ -18933,7 +18937,7 @@ public class PackageManagerService extends IPackageManager.Stub // can downgrade to reader if (writeSettings) { // Save settings now - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } if (installedStateChanged) { mSettings.writeKernelMappingLPr(deletedPs); @@ -19020,8 +19024,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); try { installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles, - outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(), - writeSettings); + outInfo == null ? null : outInfo.origUsers, writeSettings); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": " + e.getMessage()); @@ -19052,9 +19055,8 @@ public class PackageManagerService extends IPackageManager.Stub * Installs a package that's already on the system partition. */ private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString, - @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, - @Nullable PermissionsState origPermissionState, boolean writeSettings) - throws PackageManagerException { + @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) + throws PackageManagerException { final File codePath = new File(codePathString); @ParseFlags int parseFlags = mDefParseFlags @@ -19091,12 +19093,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); - // Propagate the permissions state as we do not want to drop on the floor - // runtime permissions. The update permissions method below will take - // care of removing obsolete permissions and grant install permissions. - if (origPermissionState != null) { - ps.getPermissionsState().copyFrom(origPermissionState); - } + // The update permissions method below will take care of removing obsolete permissions + // and granting install permissions. mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); final boolean applyUserRestrictions @@ -19130,7 +19128,7 @@ public class PackageManagerService extends IPackageManager.Stub } // can downgrade to reader here if (writeSettings) { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } return pkg; @@ -19204,7 +19202,7 @@ public class PackageManagerService extends IPackageManager.Stub } else { ps.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER; } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } return true; } @@ -20396,7 +20394,7 @@ public class PackageManagerService extends IPackageManager.Stub (parser1, userId1) -> { synchronized (mLock) { mSettings.readAllDomainVerificationsLPr(parser1, userId1); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } }); } catch (Exception e) { @@ -21747,6 +21745,8 @@ public class PackageManagerService extends IPackageManager.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + DumpState dumpState = new DumpState(); boolean fullPreferred = false; boolean checkin = false; @@ -21942,7 +21942,7 @@ public class PackageManagerService extends IPackageManager.Stub dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS); } else if ("write".equals(cmd)) { synchronized (mLock) { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); pw.println("Settings written."); return; } @@ -22660,7 +22660,7 @@ public class PackageManagerService extends IPackageManager.Stub // Yay, everything is now upgraded ver.forceCurrent(); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } for (PackageFreezer freezer : freezers) { @@ -22710,7 +22710,7 @@ public class PackageManagerService extends IPackageManager.Stub AttributeCache.instance().removePackage(ps.name); } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } @@ -23623,6 +23623,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { mDirtyUsers.remove(userId); mUserNeedsBadging.delete(userId); + mPermissionManager.onUserRemoved(userId); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.removeUserLPw(userId); mPendingBroadcasts.remove(userId); mInstantAppRegistry.onUserRemovedLPw(userId); @@ -23723,7 +23725,9 @@ public class PackageManagerService extends IPackageManager.Stub boolean readPermissionStateForUser(@UserIdInt int userId) { synchronized (mPackages) { + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.readPermissionStateForUserSyncLPr(userId); + mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); return mPmInternal.isPermissionUpgradeNeeded(userId); } } @@ -25179,7 +25183,7 @@ public class PackageManagerService extends IPackageManager.Stub if (async) { scheduleWriteSettingsLocked(); } else { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } } @@ -25226,7 +25230,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } mSettings.mReadExternalStorageEnforced = enforced ? Boolean.TRUE : Boolean.FALSE; - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } @@ -25740,6 +25744,17 @@ public class PackageManagerService extends IPackageManager.Stub public List<String> getMimeGroup(String packageName, String mimeGroup) { return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup); } + + /** + * Temporary method that wraps mSettings.writeLPr() and calls + * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand. + * + * TODO(zhanghai): This should be removed once we finish migration of permission storage. + */ + private void writeSettingsLPrTEMP() { + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + mSettings.writeLPr(); + } } interface PackageSender { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index de0e4b53adab..491b4fc515ce 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -34,7 +34,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.ResolveInfo; @@ -68,7 +67,6 @@ import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.permission.PermissionsState; import dalvik.system.VMRuntime; @@ -968,20 +966,6 @@ public class PackageManagerServiceUtils { } /** - * Returns the {@link PermissionsState} for the given package. If the {@link PermissionsState} - * could not be found, {@code null} will be returned. - */ - public static PermissionsState getPermissionsState( - PackageManagerInternal packageManagerInternal, AndroidPackage pkg) { - final PackageSetting packageSetting = packageManagerInternal.getPackageSetting( - pkg.getPackageName()); - if (packageSetting == null) { - return null; - } - return packageSetting.getPermissionsState(); - } - - /** * Recursively create target directory */ public static void makeDirRecursive(File targetDir, int mode) throws ErrnoException { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index acb149b9ec3d..d545bd4c1531 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -955,93 +955,6 @@ public final class Settings { } } - /* - * Update the shared user setting when a package with a shared user id is removed. The gids - * associated with each permission of the deleted package are removed from the shared user' - * gid list only if its not in use by other permissions of packages in the shared user setting. - * - * @return the affected user id - */ - @UserIdInt - int updateSharedUserPermsLPw(PackageSetting deletedPs, int userId) { - if ((deletedPs == null) || (deletedPs.pkg == null)) { - Slog.i(PackageManagerService.TAG, - "Trying to update info for null package. Just ignoring"); - return UserHandle.USER_NULL; - } - - // No sharedUserId - if (deletedPs.sharedUser == null) { - return UserHandle.USER_NULL; - } - - SharedUserSetting sus = deletedPs.sharedUser; - - int affectedUserId = UserHandle.USER_NULL; - // Update permissions - for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { - BasePermission bp = mPermissions.getPermission(eachPerm); - if (bp == null) { - continue; - } - - // Check if another package in the shared user needs the permission. - boolean used = false; - for (PackageSetting pkg : sus.packages) { - if (pkg.pkg != null - && !pkg.pkg.getPackageName().equals(deletedPs.pkg.getPackageName()) - && pkg.pkg.getRequestedPermissions().contains(eachPerm)) { - used = true; - break; - } - } - if (used) { - continue; - } - - PermissionsState permissionsState = sus.getPermissionsState(); - PackageSetting disabledPs = getDisabledSystemPkgLPr(deletedPs.pkg.getPackageName()); - - // If the package is shadowing is a disabled system package, - // do not drop permissions that the shadowed package requests. - if (disabledPs != null) { - boolean reqByDisabledSysPkg = false; - for (String permission : disabledPs.pkg.getRequestedPermissions()) { - if (permission.equals(eachPerm)) { - reqByDisabledSysPkg = true; - break; - } - } - if (reqByDisabledSysPkg) { - continue; - } - } - - // Try to revoke as an install permission which is for all users. - // The package is gone - no need to keep flags for applying policy. - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); - - if (permissionsState.revokeInstallPermission(bp) == - PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - affectedUserId = UserHandle.USER_ALL; - } - - // Try to revoke as an install permission which is per user. - if (permissionsState.revokeRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - if (affectedUserId == UserHandle.USER_NULL) { - affectedUserId = userId; - } else if (affectedUserId != userId) { - // Multiple users affected. - affectedUserId = UserHandle.USER_ALL; - } - } - } - - return affectedUserId; - } - int removePackageLPw(String name) { final PackageSetting p = mPackages.get(name); if (p != null) { @@ -5533,32 +5446,11 @@ public final class Settings { // Make sure we do not mHandler.removeMessages(userId); - for (SettingBase sb : mPackages.values()) { - revokeRuntimePermissionsAndClearFlags(sb, userId); - } - - for (SettingBase sb : mSharedUsers.values()) { - revokeRuntimePermissionsAndClearFlags(sb, userId); - } - mPermissionUpgradeNeeded.delete(userId); mVersions.delete(userId); mFingerprints.remove(userId); } - private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) { - PermissionsState permissionsState = sb.getPermissionsState(); - for (PermissionState permissionState - : permissionsState.getRuntimePermissionStates(userId)) { - BasePermission bp = mPermissions.getPermission(permissionState.getName()); - if (bp != null) { - permissionsState.revokeRuntimePermission(bp, userId); - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); - } - } - } - public void deleteUserRuntimePermissionsFile(int userId) { mPersistence.deleteForUser(UserHandle.of(userId)); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d137fd05f793..c1aebd33889c 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -84,6 +84,7 @@ import android.os.storage.StorageManager; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; import android.stats.devicepolicy.DevicePolicyEnums; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -141,7 +142,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicInteger; /** * Service for {@link UserManager}. @@ -159,6 +159,10 @@ public class UserManagerService extends IUserManager.Stub { private static final String LOG_TAG = "UserManagerService"; static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE + + // TODO(b/164159026): remove once owner_name issue on automotive is fixed + // Can be used to track getUsers() / userWithNameLU() behavior + public static final boolean DBG_CACHED_USERINFOS = false; // DO NOT SUBMIT WITH TRUE // Can be used for manual testing of id recycling private static final boolean RELEASE_DELETED_USER_ID = false; // DO NOT SUBMIT WITH TRUE @@ -272,6 +276,25 @@ public class UserManagerService extends IUserManager.Stub { private DevicePolicyManagerInternal mDevicePolicyManagerInternal; /** + * Reference to the {@link UserHandle#SYSTEM} user's UserInfo; it's {@code name} was either + * manually set, or it's {@code null}. + * + * <p>The reference is set just once, but it's {@code name} is updated when it's manually set. + */ + @GuardedBy("mUsersLock") + private UserInfo mSystemUserInfo; + + /** + * Reference to the {@link UserHandle#SYSTEM} user's UserInfo, with its {@code name} set to + * the localized value of {@code owner_name}. + * + * <p>The reference is set just once, but it's {@code name} is updated everytime the reference + * is used and the locale changed. + */ + @GuardedBy("mUsersLock") + private UserInfo mSystemUserInfoWithName; + + /** * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps. */ @VisibleForTesting @@ -444,11 +467,6 @@ public class UserManagerService extends IUserManager.Stub { } }; - // TODO(b/161915546): remove once userWithName() is fixed / removed - // Use to debug / dump when user 0 is allocated at userWithName() - public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE - public final AtomicInteger mUser0Allocations; - /** * Start an {@link IntentSender} when user is unlocked after disabling quiet mode. * @@ -638,7 +656,6 @@ public class UserManagerService extends IUserManager.Stub { LocalServices.addService(UserManagerInternal.class, mLocalService); mLockPatternUtils = new LockPatternUtils(mContext); mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING); - mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null; } void systemReady() { @@ -784,7 +801,7 @@ public class UserManagerService extends IUserManager.Stub { || (excludePreCreated && ui.preCreated)) { continue; } - users.add(userWithName(ui)); + users.add(userWithNameLU(ui)); } return users; } @@ -853,7 +870,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.name = null; userInfo.iconPath = null; } else { - userInfo = userWithName(userInfo); + userInfo = userWithNameLU(userInfo); } users.add(userInfo); } @@ -1310,26 +1327,57 @@ public class UserManagerService extends IUserManager.Stub { public UserInfo getUserInfo(@UserIdInt int userId) { checkManageOrCreateUsersPermission("query user"); synchronized (mUsersLock) { - return userWithName(getUserInfoLU(userId)); + return userWithNameLU(getUserInfoLU(userId)); } } /** * Returns a UserInfo object with the name filled in, for Owner, or the original * if the name is already set. + * + * <p>Note:</p> the Owner name is localized, so the current value must be checked every time + * this method is called. */ - private UserInfo userWithName(UserInfo orig) { - if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) { - if (DBG_ALLOCATION) { - final int number = mUser0Allocations.incrementAndGet(); - Slog.w(LOG_TAG, "System user instantiated at least " + number + " times"); - } - UserInfo withName = new UserInfo(orig); - withName.name = getOwnerName(); - return withName; - } else { - return orig; + private UserInfo userWithNameLU(UserInfo orig) { + // Only the system user uses the owner_name string. + if (orig == null || orig.id != UserHandle.USER_SYSTEM) return orig; + + if (mSystemUserInfo == null) { + mSystemUserInfo = orig; + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Set mSystemUserInfo:" + mSystemUserInfo.toFullString()); + } } + + if (mSystemUserInfo.name != null) { + if (DBG_CACHED_USERINFOS) { + Slog.v(LOG_TAG, "Returning mSystemUserInfo: " + mSystemUserInfo.toFullString()); + } + return mSystemUserInfo; + } + + final String ownerName = getOwnerName(); + + if (mSystemUserInfoWithName == null) { + mSystemUserInfoWithName = new UserInfo(orig); + mSystemUserInfoWithName.name = ownerName; + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Set mSystemUserInfoWithName: " + + mSystemUserInfoWithName.toFullString()); + } + } else if (!TextUtils.equals(ownerName, mSystemUserInfoWithName.name)) { + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Updating mSystemUserInfoWithName.name from " + + mSystemUserInfoWithName.name + " to " + ownerName); + } + mSystemUserInfoWithName.name = ownerName; + } + + if (DBG_CACHED_USERINFOS) { + Slog.v(LOG_TAG, "Returning mSystemUserInfoWithName:" + + mSystemUserInfoWithName.toFullString()); + } + return mSystemUserInfoWithName; } /** Returns whether the given user type is one of the FULL user types. */ @@ -1482,7 +1530,7 @@ public class UserManagerService extends IUserManager.Stub { } final int userId = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mUsersLock) { - UserInfo userInfo = userWithName(getUserInfoLU(userId)); + UserInfo userInfo = userWithNameLU(getUserInfoLU(userId)); return userInfo == null ? "" : userInfo.name; } } @@ -1597,6 +1645,13 @@ public class UserManagerService extends IUserManager.Stub { Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); return null; } + + if (DBG_CACHED_USERINFOS && userId == UserHandle.USER_SYSTEM && userData != null + && userData.info != mSystemUserInfo) { + Slog.wtf(LOG_TAG, "getUserInfoLU(): system user on userData (" + userData.info + + ") is not the same as mSystemUserInfo (" + mSystemUserInfo + ")"); + } + return userData != null ? userData.info : null; } @@ -4855,8 +4910,15 @@ public class UserManagerService extends IUserManager.Stub { pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode()); pw.println(" User version: " + mUserVersion); pw.println(" Owner name: " + getOwnerName()); - if (DBG_ALLOCATION) { - pw.println(" System user allocations: " + mUser0Allocations.get()); + if (mSystemUserInfo == null) { + pw.println(" (mSystemUserInfo not set)"); + } else { + pw.println(" System user: " + mSystemUserInfo.toFullString()); + } + if (mSystemUserInfoWithName == null) { + pw.println(" (mSystemUserInfoWithName not set)"); + } else { + pw.println(" System user (with name): " + mSystemUserInfoWithName.toFullString()); } // Dump UserTypes diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index cfa0449aaf33..962638b4f63c 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -38,7 +38,6 @@ import android.util.Slog; import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; -import com.android.server.pm.PackageSetting; import com.android.server.pm.PackageSettingBase; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -420,8 +419,7 @@ public final class BasePermission { } public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg, - PackageSetting pkgSetting) { - final PermissionsState permsState = pkgSetting.getPermissionsState(); + PermissionsState permsState) { int index = pkg.getRequestedPermissions().indexOf(name); if (!permsState.hasRequestedPermission(name) && index == -1) { throw new SecurityException("Package " + pkg.getPackageName() diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 1be74154b53a..f5dd918a18f3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -162,6 +162,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -227,6 +228,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; + /** Maps from App ID to PermissionsState */ + private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>(); + /** Permission controller: User space permission management */ private PermissionControllerManager mPermissionControllerManager; @@ -671,11 +675,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (pkg == null) { return 0; } - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { - return 0; - } synchronized (mLock) { if (mSettings.getPermissionLocked(permName) == null) { return 0; @@ -684,7 +683,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { return 0; } - PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return 0; + } return permissionsState.getPermissionFlags(permName, userId); } @@ -771,9 +774,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - packageName); - if (pkg == null || ps == null) { + if (pkg == null) { Log.e(TAG, "Unknown package: " + packageName); return; } @@ -789,7 +790,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return; + } + final boolean hadState = permissionsState.getRuntimePermissionState(permName, userId) != null; if (!hadState) { @@ -864,12 +870,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean[] changed = new boolean[1]; mPackageManagerInt.forEachPackage(pkg -> { - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - final PermissionsState permissionsState = ps.getPermissionsState(); changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions( userId, effectiveFlagMask, effectiveFlagValues); mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid()); @@ -923,12 +928,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final int uid = UserHandle.getUid(userId, pkg.getUid()); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return PackageManager.PERMISSION_DENIED; } - final PermissionsState permissionsState = ps.getPermissionsState(); if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) { return PackageManager.PERMISSION_GRANTED; @@ -1139,9 +1143,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - final PermissionsState permissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg); + final PermissionsState permissionsState = getPermissionsState(pkg); if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); return null; } @@ -1451,7 +1455,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + return; + } + + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1464,8 +1474,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); - final PermissionsState permissionsState = ps.getPermissionsState(); - final int flags = permissionsState.getPermissionFlags(permName, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { Log.e(TAG, "Cannot grant system fixed permission " @@ -1599,9 +1607,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { "revokeRuntimePermission"); final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - packageName); - if (pkg == null || ps == null) { + if (pkg == null) { Log.e(TAG, "Unknown package: " + packageName); return; } @@ -1613,7 +1619,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + return; + } + + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1624,8 +1636,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } - final PermissionsState permissionsState = ps.getPermissionsState(); - final int flags = permissionsState.getPermissionFlags(permName, userId); // Only the system may revoke SYSTEM_FIXED permissions. if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 @@ -2456,14 +2466,36 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + private void onUserRemoved(@UserIdInt int userId) { + synchronized (mLock) { + final int appIdStatesSize = mAppIdStates.size(); + for (int i = 0; i < appIdStatesSize; i++) { + PermissionsState permissionsState = mAppIdStates.valueAt(i); + for (PermissionState permissionState + : permissionsState.getRuntimePermissionStates(userId)) { + BasePermission bp = mSettings.getPermission(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + } + } + } + } + } + @NonNull private Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) { final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName); if (ps == null) { - return null; + return Collections.emptySet(); + } + final PermissionsState permissionsState = getPermissionsState(ps); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return Collections.emptySet(); } - final PermissionsState permissionsState = ps.getPermissionsState(); if (!ps.getInstantApp(userId)) { return permissionsState.getPermissions(userId); } else { @@ -2503,7 +2535,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return null; } - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(ps); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return null; + } return permissionsState.computeGids(userId); } @@ -2541,8 +2577,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return; } - - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getOrCreatePermissionsState(ps); final int[] userIds = getAllUserIds(); @@ -2614,8 +2649,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { // changed runtime permissions here are promotion of an install to // runtime and revocation of a runtime from a shared user. synchronized (mLock) { - updatedUserIds = revokeUnusedSharedUserPermissionsLocked(ps.getSharedUser(), - userIds); + updatedUserIds = revokeUnusedSharedUserPermissionsLocked( + ps.getSharedUser().getPackages(), permissionsState, userIds); if (!ArrayUtils.isEmpty(updatedUserIds)) { runtimePermissionsRevoked = true; } @@ -3091,6 +3126,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { updatedUserIds); } + // TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important + // for shared users. // Persist the runtime permissions state for users with changes. If permissions // were revoked because no app in the shared user declares them we have to // write synchronously to avoid losing runtime permissions state. @@ -3554,37 +3591,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PackageSetting disabledPs = mPackageManagerInt .getDisabledSystemPackage(pkg.getPackageName()); final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg; - if (disabledPs != null - && disabledPs.getPermissionsState().hasInstallPermission(perm)) { - // If the original was granted this permission, we take - // that grant decision as read and propagate it to the - // update. - if ((privilegedPermission && disabledPs.isPrivileged()) - || (oemPermission && disabledPs.isOem() - && canGrantOemPermission(disabledPs, perm))) { - allowed = true; - } - } else { - // The system apk may have been updated with an older - // version of the one on the data partition, but which - // granted a new system permission that it didn't have - // before. In this case we do want to allow the app to - // now get the new permission if the ancestral apk is - // privileged to get it. - if (disabledPs != null && disabledPkg != null - && isPackageRequestingPermission(disabledPkg, perm) - && ((privilegedPermission && disabledPs.isPrivileged()) - || (oemPermission && disabledPs.isOem() - && canGrantOemPermission(disabledPs, perm)))) { - allowed = true; - } + if (disabledPkg != null && isPackageRequestingPermission(disabledPkg, perm) + && ((privilegedPermission && disabledPkg.isPrivileged()) + || (oemPermission && canGrantOemPermission(disabledPkg, + perm)))) { + allowed = true; } } else { - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); allowed = (privilegedPermission && pkg.isPrivileged()) - || (oemPermission && pkg.isOem() - && canGrantOemPermission(ps, perm)); + || (oemPermission && canGrantOemPermission(pkg, perm)); } // In any case, don't grant a privileged permission to privileged vendor apps, if // the permission's protectionLevel does not have the extra 'vendorPrivileged' @@ -3735,16 +3750,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { return false; } - private static boolean canGrantOemPermission(PackageSetting ps, String permission) { - if (!ps.isOem()) { + private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) { + if (!pkg.isOem()) { return false; } // all oem permissions must explicitly be granted or denied final Boolean granted = - SystemConfig.getInstance().getOemPermissions(ps.name).get(permission); + SystemConfig.getInstance().getOemPermissions(pkg.getPackageName()).get(permission); if (granted == null) { throw new IllegalStateException("OEM permission" + permission + " requested by package " - + ps.name + " must be explicitly declared granted or not"); + + pkg.getPackageName() + " must be explicitly declared granted or not"); } return Boolean.TRUE == granted; } @@ -3757,12 +3772,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // Legacy apps have the permission and get user consent on launch. - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return false; } - final PermissionsState permissionsState = ps.getPermissionsState(); return permissionsState.isPermissionReviewRequired(userId); } @@ -3787,14 +3801,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId, String[] grantedPermissions, int callingUid, PermissionCallback callback) { - PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - PermissionsState permissionsState = ps.getPermissionsState(); - final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -3838,9 +3850,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg, @UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid, @PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) { - final PermissionsState permissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg); + final PermissionsState permissionsState = getPermissionsState(pkg); if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } @@ -3958,9 +3970,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int j = 0; j < oldGrantedCount; j++) { final String permission = oldPermsForUser.valueAt(j); // Sometimes we create a new permission state instance during update. - final PermissionsState newPermissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, - pkg); + final PermissionsState newPermissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + continue; + } if (!newPermissionsState.hasPermission(permission, userId)) { callback.onPermissionRevoked(pkg.getUid(), userId, null); break; @@ -3970,12 +3984,100 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + @UserIdInt + private int revokeSharedUserPermissionsForDeletedPackage(@NonNull PackageSetting deletedPs, + @UserIdInt int userId) { + if ((deletedPs == null) || (deletedPs.pkg == null)) { + Slog.i(TAG, "Trying to update info for null package. Just ignoring"); + return UserHandle.USER_NULL; + } + + SharedUserSetting sus = deletedPs.getSharedUser(); + + // No sharedUserId + if (sus == null) { + return UserHandle.USER_NULL; + } + + int affectedUserId = UserHandle.USER_NULL; + // Update permissions + for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { + BasePermission bp = mSettings.getPermission(eachPerm); + if (bp == null) { + continue; + } + + // Check if another package in the shared user needs the permission. + boolean used = false; + final List<AndroidPackage> pkgs = sus.getPackages(); + if (pkgs != null) { + for (AndroidPackage pkg : pkgs) { + if (pkg != null + && !pkg.getPackageName().equals(deletedPs.pkg.getPackageName()) + && pkg.getRequestedPermissions().contains(eachPerm)) { + used = true; + break; + } + } + } + if (used) { + continue; + } + + PermissionsState permissionsState = getPermissionsState(deletedPs.pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()); + continue; + } + + PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage( + deletedPs.pkg.getPackageName()); + + // If the package is shadowing is a disabled system package, + // do not drop permissions that the shadowed package requests. + if (disabledPs != null) { + boolean reqByDisabledSysPkg = false; + for (String permission : disabledPs.pkg.getRequestedPermissions()) { + if (permission.equals(eachPerm)) { + reqByDisabledSysPkg = true; + break; + } + } + if (reqByDisabledSysPkg) { + continue; + } + } + + // Try to revoke as an install permission which is for all users. + // The package is gone - no need to keep flags for applying policy. + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + + if (permissionsState.revokeInstallPermission(bp) + == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + affectedUserId = UserHandle.USER_ALL; + } + + // Try to revoke as a runtime permission which is per user. + if (permissionsState.revokeRuntimePermission(bp, userId) + == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + if (affectedUserId == UserHandle.USER_NULL) { + affectedUserId = userId; + } else if (affectedUserId != userId) { + // Multiple users affected. + affectedUserId = UserHandle.USER_ALL; + } + } + } + + return affectedUserId; + } + @GuardedBy("mLock") private int[] revokeUnusedSharedUserPermissionsLocked( - SharedUserSetting suSetting, int[] allUserIds) { + List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) { // Collect all used permissions in the UID final ArraySet<String> usedPermissions = new ArraySet<>(); - final List<AndroidPackage> pkgList = suSetting.getPackages(); if (pkgList == null || pkgList.size() == 0) { return EmptyArray.INT; } @@ -3993,7 +4095,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - PermissionsState permissionsState = suSetting.getPermissionsState(); // Prune install permissions List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); final int installPermCount = installPermStates.size(); @@ -4279,12 +4380,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } else { mPackageManagerInt.forEachPackage(p -> { - PackageSetting ps = mPackageManagerInt.getPackageSetting( - p.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(p); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + p.getPackageName()); return; } - PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.getInstallPermissionState(bp.getName()) != null) { permissionsState.revokeInstallPermission(bp); permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, @@ -4695,6 +4795,67 @@ public class PermissionManagerService extends IPermissionManager.Stub { return mBackgroundPermissions; } + @Nullable + private PermissionsState getPermissionsState(@NonNull PackageSetting ps) { + return getPermissionsState(ps.getAppId()); + } + + @Nullable + private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) { + return getPermissionsState(pkg.getUid()); + } + + @Nullable + private PermissionsState getPermissionsState(int appId) { + synchronized (mLock) { + return mAppIdStates.get(appId); + } + } + + @Nullable + private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) { + return getOrCreatePermissionsState(ps.getAppId()); + } + + @Nullable + private PermissionsState getOrCreatePermissionsState(int appId) { + synchronized (mLock) { + PermissionsState state = mAppIdStates.get(appId); + if (state == null) { + state = new PermissionsState(); + mAppIdStates.put(appId, state); + } + return state; + } + } + + private void removePermissionsState(int appId) { + synchronized (mLock) { + mAppIdStates.remove(appId); + } + } + + private void readPermissionsStateFromPackageSettings() { + mPackageManagerInt.forEachPackageSetting(ps -> { + synchronized (mLock) { + mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState())); + } + }); + } + + private void writePermissionsStateToPackageSettings() { + mPackageManagerInt.forEachPackageSetting(ps -> { + synchronized (mLock) { + final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId()); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + ps.name); + return; + } + ps.getPermissionsState().copyFrom(permissionsState); + } + }); + } + private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal { @Override public void systemReady() { @@ -4726,6 +4887,29 @@ public class PermissionManagerService extends IPermissionManager.Stub { public void removeAllPermissions(AndroidPackage pkg, boolean chatty) { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } + @Override + public void readPermissionsStateFromPackageSettingsTEMP() { + PermissionManagerService.this.readPermissionsStateFromPackageSettings(); + } + @Override + public void writePermissionsStateToPackageSettingsTEMP() { + PermissionManagerService.this.writePermissionsStateToPackageSettings(); + } + @Override + public void onUserRemoved(@UserIdInt int userId) { + PermissionManagerService.this.onUserRemoved(userId); + } + @Override + public void removePermissionsStateTEMP(int appId) { + PermissionManagerService.this.removePermissionsState(appId); + } + @Override + @UserIdInt + public int revokeSharedUserPermissionsForDeletedPackageTEMP( + @NonNull PackageSetting deletedPs, @UserIdInt int userId) { + return PermissionManagerService.this.revokeSharedUserPermissionsForDeletedPackage( + deletedPs, userId); + } @NonNull @Override public Set<String> getGrantedPermissions(@NonNull String packageName, diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index cfa371ddbad3..f319bf495e8b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; +import com.android.server.pm.PackageSetting; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.ArrayList; @@ -265,6 +266,52 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); /** + * Read {@code PermissionsState} from package settings. + * + * TODO(zhanghai): This is a temporary method because we should not expose + * {@code PackageSetting} which is a implementation detail that permission should not know. + * Instead, it should retrieve the legacy state via a defined API. + */ + public abstract void readPermissionsStateFromPackageSettingsTEMP(); + + /** + * Write {@code PermissionsState} from to settings. + * + * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence + * for permission. + */ + public abstract void writePermissionsStateToPackageSettingsTEMP(); + + /** + * Notify that a user has been removed and its permission state should be removed as well. + */ + public abstract void onUserRemoved(@UserIdInt int userId); + + /** + * Remove the {@code PermissionsState} associated with an app ID, called the same time as the + * removal of a {@code PackageSetitng}. + * + * TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app + * ID removal via API. + */ + public abstract void removePermissionsStateTEMP(int appId); + + /** + * Update the shared user setting when a package with a shared user id is removed. The gids + * associated with each permission of the deleted package are removed from the shared user' + * gid list only if its not in use by other permissions of packages in the shared user setting. + * + * TODO(zhanghai): We should not need this when permission no longer sees an incomplete package + * state where the updated system package is uninstalled but the disabled system package is yet + * to be installed. Then we should handle this in restorePermissionState(). + * + * @return the affected user id, may be a real user ID, USER_ALL, or USER_NULL when none. + */ + @UserIdInt + public abstract int revokeSharedUserPermissionsForDeletedPackageTEMP( + @NonNull PackageSetting deletedPs, @UserIdInt int userId); + + /** * Get all the permissions granted to a package. */ @NonNull diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java index 6dc1d6921dbb..0abeac890df1 100644 --- a/services/core/java/com/android/server/storage/StorageSessionController.java +++ b/services/core/java/com/android/server/storage/StorageSessionController.java @@ -336,11 +336,12 @@ public final class StorageSessionController { } /** - * Returns {@code true} if {@code vol} is an emulated or public volume, + * Returns {@code true} if {@code vol} is an emulated or visible public volume, * {@code false} otherwise **/ public static boolean isEmulatedOrPublic(VolumeInfo vol) { - return vol.type == VolumeInfo.TYPE_EMULATED || vol.type == VolumeInfo.TYPE_PUBLIC; + return vol.type == VolumeInfo.TYPE_EMULATED + || (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isVisible()); } /** Exception thrown when communication with the {@link ExternalStorageService} fails. */ diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 64fa6ca590d2..9316c4657826 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4664,7 +4664,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // case where this is the top activity in a pinned stack. final boolean isTop = this == stack.getTopNonFinishingActivity(); final boolean isTopNotPinnedStack = stack.isAttached() - && stack.getDisplayArea().isTopNotPinnedStack(stack); + && stack.getDisplayArea().isTopNotFinishNotPinnedStack(stack); final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this, visibleIgnoringKeyguard, isTop && isTopNotPinnedStack); @@ -4806,6 +4806,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Slog.v(TAG_VISIBILITY, "Start visible activity, " + this); } setState(STARTED, "makeActiveIfNeeded"); + + // Update process info while making an activity from invisible to visible, to make + // sure the process state is updated to foreground. + if (app != null) { + app.updateProcessInfo(false /* updateServiceConnectionActivities */, + true /* activityChange */, true /* updateOomAdj */, + true /* addPendingTopUid */); + } + try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, StartActivityItem.obtain()); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2e03cb80b189..40fc25b41d9f 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -246,6 +246,9 @@ public class DisplayPolicy { | View.STATUS_BAR_TRANSPARENT | View.NAVIGATION_BAR_TRANSPARENT; + private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR}; + private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR}; + private final WindowManagerService mService; private final Context mContext; private final Context mUiContext; @@ -3353,8 +3356,15 @@ public class DisplayPolicy { return; } + final InsetsState requestedState = controlTarget.getRequestedInsetsState(); + final @InsetsType int restorePositionTypes = + (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) + ? Type.navigationBars() : 0) + | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) + ? Type.statusBars() : 0); + if (swipeTarget == mNavigationBar - && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) { + && (restorePositionTypes & Type.navigationBars()) != 0) { // Don't show status bar when swiping on already visible navigation bar. // But restore the position of navigation bar if it has been moved by the control // target. @@ -3362,14 +3372,13 @@ public class DisplayPolicy { return; } - int insetsTypesToShow = Type.systemBars(); - if (controlTarget.canShowTransient()) { - insetsTypesToShow &= ~mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap( - new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); - } - if (insetsTypesToShow != 0) { - controlTarget.showInsets(insetsTypesToShow, false); + // Show transient bars if they are hidden; restore position if they are visible. + mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE); + controlTarget.showInsets(restorePositionTypes, false); + } else { + // Restore visibilities and positions of system bars. + controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false); } } else { boolean sb = mStatusBarController.checkShowTransientBarLw(); @@ -3770,8 +3779,7 @@ public class DisplayPolicy { // we're no longer on the Keyguard and the screen is ready. We can now request the bars. mPendingPanicGestureUptime = 0; if (!isNavBarEmpty(vis)) { - mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap( - new int[] {ITYPE_NAVIGATION_BAR})); + mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 0fe9735c9e46..cec797c43bcd 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -227,6 +227,11 @@ final class InputMonitor { WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory) { + if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) { + throw new IllegalArgumentException("Illegal input consumer : " + name + + ", display: " + mDisplayId); + } + if (mInputConsumers.containsKey(name)) { throw new IllegalStateException("Existing input consumer found with name: " + name + ", display: " + mDisplayId); @@ -256,6 +261,11 @@ final class InputMonitor { // stack, and we need FLAG_NOT_TOUCH_MODAL to ensure other events fall through consumer.mWindowHandle.layoutParamsFlags |= FLAG_NOT_TOUCH_MODAL; break; + case INPUT_CONSUMER_RECENTS_ANIMATION: + break; + default: + throw new IllegalArgumentException("Illegal input consumer : " + name + + ", display: " + mDisplayId); } addInputConsumer(name, consumer); } @@ -463,9 +473,6 @@ final class InputMonitor { mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); - if (mAddWallpaperInputConsumerHandle) { - mWallpaperInputConsumer.show(mInputTransaction, 0); - } if (!mUpdateInputWindowsImmediately) { mDisplayContent.getPendingTransaction().merge(mInputTransaction); mDisplayContent.scheduleAnimation(); diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index 3ffc26a7a8ad..5e7ed3f80e43 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.inputmethodservice.InputMethodService; +import android.view.InsetsState; import android.view.WindowInsets.Type.InsetsType; /** @@ -38,6 +39,13 @@ interface InsetsControlTarget { } /** + * @return The requested {@link InsetsState} of this target. + */ + default InsetsState getRequestedInsetsState() { + return InsetsState.EMPTY; + } + + /** * Instructs the control target to show inset sources. * * @param types to specify which types of insets source window should be shown. diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index b7287e718bd6..18a25033b1e6 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -42,7 +42,6 @@ import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.ViewRootImpl; -import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimationControlListener; @@ -153,15 +152,13 @@ class InsetsPolicy { return provider != null && provider.hasWindow() && !provider.getSource().isVisible(); } - @InsetsType int showTransient(IntArray types) { - @InsetsType int showingTransientTypes = 0; + void showTransient(@InternalInsetsType int[] types) { boolean changed = false; - for (int i = types.size() - 1; i >= 0; i--) { - final int type = types.get(i); + for (int i = types.length - 1; i >= 0; i--) { + final @InternalInsetsType int type = types[i]; if (!isHidden(type)) { continue; } - showingTransientTypes |= InsetsState.toPublicType(type); if (mShowingTransientTypes.indexOf(type) != -1) { continue; } @@ -189,7 +186,6 @@ class InsetsPolicy { } }); } - return showingTransientTypes; } void hideTransient() { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 0529abf89f6e..50c269e23c53 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3820,7 +3820,10 @@ class Task extends WindowContainer<WindowContainer> { @Override boolean fillsParent() { - return matchParentBounds(); + // From the perspective of policy, we still want to report that this task fills parent + // in fullscreen windowing mode even it doesn't match parent bounds because there will be + // letterbox around its real content. + return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds(); } @Override diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 32511108836e..6550167683a0 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1489,9 +1489,13 @@ final class TaskDisplayArea extends DisplayArea<Task> { return stack == getTopStack(); } - boolean isTopNotPinnedStack(Task stack) { + boolean isTopNotFinishNotPinnedStack(Task stack) { for (int i = getStackCount() - 1; i >= 0; --i) { final Task current = getStackAt(i); + final ActivityRecord topAct = current.getTopNonFinishingActivity(); + if (topAct == null) { + continue; + } if (!current.inPinnedWindowingMode()) { return current == stack; } diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java index 61e9e5082d17..5e81e4008680 100644 --- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java +++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java @@ -24,8 +24,6 @@ import android.annotation.NonNull; import android.util.ArrayMap; import android.util.Slog; -import com.android.server.wm.WindowManagerService.H; - import java.io.PrintWriter; /** @@ -102,7 +100,13 @@ class UnknownAppVisibilityController { if (DEBUG_UNKNOWN_APP_VISIBILITY) { Slog.d(TAG, "App launched activity=" + activity); } - mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME); + // If the activity was started with launchTaskBehind, the lifecycle will goes to paused + // directly, and the process will pass onResume, so we don't need to waiting resume for it. + if (!activity.mLaunchTaskBehind) { + mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME); + } else { + mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RELAYOUT); + } } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index cd222a97f4d9..bf900f7a91c8 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.INPUT_CONSUMER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.MANAGE_APP_TOKENS; @@ -929,7 +930,7 @@ public class WindowManagerService extends IWindowManager.Stub private void setShadowRenderer() { mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(), - DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0; + DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; } PowerManager mPowerManager; @@ -5861,6 +5862,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void createInputConsumer(IBinder token, String name, int displayId, InputChannel inputChannel) { + if (!mAtmInternal.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("createInputConsumer requires INPUT_CONSUMER permission"); + } + synchronized (mGlobalLock) { DisplayContent display = mRoot.getDisplayContent(displayId); if (display != null) { @@ -5872,6 +5878,11 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean destroyInputConsumer(String name, int displayId) { + if (!mAtmInternal.isCallerRecents(Binder.getCallingUid()) + && mContext.checkCallingOrSelfPermission(INPUT_CONSUMER) != PERMISSION_GRANTED) { + throw new SecurityException("destroyInputConsumer requires INPUT_CONSUMER permission"); + } + synchronized (mGlobalLock) { DisplayContent display = mRoot.getDisplayContent(displayId); if (display != null) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 49e623d8dd11..0e455d2a5aa6 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -728,7 +728,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * @return The insets state as requested by the client, i.e. the dispatched insets state * for which the visibilities are overridden with what the client requested. */ - InsetsState getRequestedInsetsState() { + @Override + public InsetsState getRequestedInsetsState() { return mRequestedInsetsState; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6154bef2bda3..e49c5abf59f3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1566,7 +1566,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * Creates a new {@link CallerIdentity} object to represent the caller's identity. */ - private CallerIdentity getCallerIdentity(String callerPackage) { + private CallerIdentity getCallerIdentity() { + final int callerUid = mInjector.binderGetCallingUid(); + return new CallerIdentity(callerUid, null, null); + } + + /** + * Creates a new {@link CallerIdentity} object to represent the caller's identity. + */ + private CallerIdentity getCallerIdentity(@NonNull String callerPackage) { final int callerUid = mInjector.binderGetCallingUid(); if (!isCallingFromPackage(callerPackage, callerUid)) { @@ -2127,6 +2135,81 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { reqPolicy, /* permission= */ null); } + @NonNull ActiveAdmin getDeviceOwnerOfCallerLocked(final CallerIdentity caller) { + ensureLocked(); + ComponentName doComponent = mOwners.getDeviceOwnerComponent(); + Preconditions.checkState(doComponent != null, + String.format("No device owner for user %d", caller.getUid())); + + // Use the user ID of the caller instead of mOwners.getDeviceOwnerUserId() because + // secondary, affiliated users will have their own admin. + ActiveAdmin doAdmin = getUserData(caller.getUserId()).mAdminMap.get(doComponent); + Preconditions.checkState(doAdmin != null, + String.format("Device owner %s for user %d not found", doComponent, + caller.getUid())); + + Preconditions.checkSecurity(doAdmin.getUid() == caller.getUid(), + String.format("Admin %s is not owned by uid %d, but uid %d", doComponent, + caller.getUid(), doAdmin.getUid())); + + Preconditions.checkSecurity(doAdmin.info.getComponent().equals(caller.getComponentName()), + String.format("Caller component %s is not device owner", + caller.getComponentName())); + + return doAdmin; + } + + @NonNull ActiveAdmin getProfileOwnerOfCallerLocked(final CallerIdentity caller) { + ensureLocked(); + final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(caller.getUserId()); + + Preconditions.checkState(poAdminComponent != null, + String.format("No profile owner for user %d", caller.getUid())); + + ActiveAdmin poAdmin = getUserData(caller.getUserId()).mAdminMap.get(poAdminComponent); + Preconditions.checkState(poAdmin != null, + String.format("No device profile owner for caller %d", caller.getUid())); + + Preconditions.checkSecurity(poAdmin.getUid() == caller.getUid(), + String.format("Admin %s is not owned by uid %d", poAdminComponent, + caller.getUid())); + + Preconditions.checkSecurity(poAdmin.info.getComponent().equals(caller.getComponentName()), + String.format("Caller component %s is not profile owner", + caller.getComponentName())); + + return poAdmin; + } + + @NonNull ActiveAdmin getOrganizationOwnedProfileOwnerLocked(final CallerIdentity caller) { + final ActiveAdmin profileOwner = getProfileOwnerOfCallerLocked(caller); + + Preconditions.checkSecurity( + mOwners.isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId()), + String.format("Admin %s is not of an org-owned device", + profileOwner.info.getComponent())); + + return profileOwner; + } + + @NonNull ActiveAdmin getProfileOwnerOrDeviceOwnerLocked(final CallerIdentity caller) { + ensureLocked(); + // Try to find an admin which can use reqPolicy + final ComponentName poAdminComponent = mOwners.getProfileOwnerComponent(caller.getUserId()); + final ComponentName doAdminComponent = mOwners.getDeviceOwnerComponent(); + + if (poAdminComponent == null && doAdminComponent == null) { + throw new IllegalStateException( + String.format("No profile or device owner for user %d", caller.getUid())); + } + + if (poAdminComponent != null) { + return getProfileOwnerOfCallerLocked(caller); + } + + return getDeviceOwnerOfCallerLocked(caller); + } + /** * Finds an active admin for the caller then checks {@code permission} if admin check failed. * @@ -2145,9 +2228,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid); if (result != null) { return result; - } else if (permission != null - && (mContext.checkCallingPermission(permission) - == PackageManager.PERMISSION_GRANTED)) { + } else if (permission != null && hasCallingPermission(permission)) { return null; } @@ -2844,9 +2925,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle, Bundle onEnableData) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.MANAGE_DEVICE_ADMINS, null); - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); DevicePolicyData policy = getUserData(userHandle); DeviceAdminInfo info = findAdmin(adminReceiver, userHandle, @@ -2986,7 +3070,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return false; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null; } @@ -2997,7 +3085,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return false; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { DevicePolicyData policyData = getUserData(userHandle); return policyData.mRemovingAdmins.contains(adminReceiver); @@ -3009,7 +3101,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return false; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(adminReceiver); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (administrator == null) { @@ -3025,8 +3121,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return Collections.EMPTY_LIST; } + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); - enforceFullCrossUsersPermission(userHandle); synchronized (getLockObject()) { DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); @@ -3046,7 +3145,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return false; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); @@ -3156,8 +3259,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); enforceUserUnlocked(userHandle); + synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (admin == null) { @@ -3309,7 +3416,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return PASSWORD_QUALITY_UNSPECIFIED; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { int mode = PASSWORD_QUALITY_UNSPECIFIED; @@ -3521,7 +3632,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return 0L; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { long timeout = 0L; @@ -3547,12 +3662,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) { - final int userId = UserHandle.getCallingUserId(); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(identity)); List<String> changedProviders = null; synchronized (getLockObject()) { - ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin activeAdmin = getProfileOwnerOfCallerLocked(identity); if (activeAdmin.crossProfileWidgetProviders == null) { activeAdmin.crossProfileWidgetProviders = new ArrayList<>(); } @@ -3560,7 +3675,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!providers.contains(packageName)) { providers.add(packageName); changedProviders = new ArrayList<>(providers); - saveSettingsLocked(userId); + saveSettingsLocked(identity.getUserId()); } } @@ -3570,7 +3685,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); if (changedProviders != null) { - mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders); + mLocalService.notifyCrossProfileProvidersChanged(identity.getUserId(), + changedProviders); return true; } @@ -3579,12 +3695,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) { - final int userId = UserHandle.getCallingUserId(); + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(identity)); List<String> changedProviders = null; synchronized (getLockObject()) { - ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin activeAdmin = getProfileOwnerOfCallerLocked(identity); if (activeAdmin.crossProfileWidgetProviders == null || activeAdmin.crossProfileWidgetProviders.isEmpty()) { return false; @@ -3592,7 +3708,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { List<String> providers = activeAdmin.crossProfileWidgetProviders; if (providers.remove(packageName)) { changedProviders = new ArrayList<>(providers); - saveSettingsLocked(userId); + saveSettingsLocked(identity.getUserId()); } } @@ -3602,7 +3718,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); if (changedProviders != null) { - mLocalService.notifyCrossProfileProvidersChanged(userId, changedProviders); + mLocalService.notifyCrossProfileProvidersChanged(identity.getUserId(), + changedProviders); return true; } @@ -3611,9 +3728,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public List<String> getCrossProfileWidgetProviders(ComponentName admin) { + final CallerIdentity identity = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(identity)); + synchronized (getLockObject()) { - ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(admin, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin activeAdmin = getProfileOwnerOfCallerLocked(identity); if (activeAdmin.crossProfileWidgetProviders == null || activeAdmin.crossProfileWidgetProviders.isEmpty()) { return null; @@ -3656,7 +3775,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return 0L; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { return getPasswordExpirationLocked(who, userHandle, parent); } @@ -3862,7 +3985,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return 0; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { if (who != null) { final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); @@ -3902,7 +4029,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { new PasswordMetrics(CREDENTIAL_TYPE_NONE); } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(); synchronized (getLockObject()) { List<ActiveAdmin> admins = @@ -3919,7 +4050,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return true; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); enforceUserUnlocked(userHandle, parent); synchronized (getLockObject()) { @@ -3951,7 +4085,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return true; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); enforceManagedProfile(userHandle, "call APIs refering to the parent profile"); synchronized (getLockObject()) { @@ -3970,7 +4107,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return true; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); enforceNotManagedProfile(userHandle, "check password sufficiency"); enforceUserUnlocked(userHandle); @@ -4058,12 +4198,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mLockPatternUtils.hasSecureLockScreen()) { return 0; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { - if (!isCallerWithSystemUid()) { + if (!isSystemUid(identity)) { // This API can be called by an active device admin or by keyguard code. - if (mContext.checkCallingPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE) - != PackageManager.PERMISSION_GRANTED) { + if (!hasCallingPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE)) { getActiveAdminForCallerLocked( null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent); } @@ -4106,7 +4249,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return 0; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { ActiveAdmin admin = (who != null) ? getActiveAdminUncheckedLocked(who, userHandle, parent) @@ -4120,7 +4267,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return UserHandle.USER_NULL; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked( userHandle, parent); @@ -4191,8 +4342,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to // set password to an unsecured user. - if (mContext.checkCallingPermission(permission.RESET_PASSWORD) - == PackageManager.PERMISSION_GRANTED) { + if (hasCallingPermission(permission.RESET_PASSWORD)) { return setPasswordPrivileged(password, flags, callingUid); } @@ -4392,7 +4542,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return 0; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { if (who != null) { final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent); @@ -4465,12 +4619,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS; } + Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId)); + if (!mLockPatternUtils.hasSecureLockScreen()) { // No strong auth timeout on devices not supporting the // {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature return 0; } - enforceFullCrossUsersPermission(userId); synchronized (getLockObject()) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId, parent); @@ -4504,8 +4662,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void lockNow(int flags, boolean parent) { - if (!mHasFeature && mContext.checkCallingPermission(android.Manifest.permission.LOCK_DEVICE) - != PackageManager.PERMISSION_GRANTED) { + if (!mHasFeature && !hasCallingPermission(permission.LOCK_DEVICE)) { return; } @@ -4597,8 +4754,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) { - if (mContext.checkCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK) - == PackageManager.PERMISSION_GRANTED) { + if (hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)) { return; } enforceProfileOrDeviceOwner(who); @@ -5677,8 +5833,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - - enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId()); + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(isSystemUid(identity) || isRootUid(identity) + || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)); final ActiveAdmin admin; synchronized (getLockObject()) { @@ -5854,8 +6011,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { if (who == null) { if ((frpManagementAgentUid != mInjector.binderGetCallingUid()) - && (mContext.checkCallingPermission(permission.MASTER_CLEAR) - != PackageManager.PERMISSION_GRANTED)) { + && !hasCallingPermission(permission.MASTER_CLEAR)) { throw new SecurityException( "Must be called by the FRP management agent on device"); } @@ -5893,9 +6049,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - enforceFullCrossUsersPermission(userHandle); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.BIND_DEVICE_ADMIN, null); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = comp != null + ? getCallerIdentity(comp) + : getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle); @@ -5972,13 +6132,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void reportFailedPasswordAttempt(int userHandle) { - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); if (!isSeparateProfileChallengeEnabled(userHandle)) { enforceNotManagedProfile(userHandle, "report failed password attempt if separate profile challenge is not in place"); } - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.BIND_DEVICE_ADMIN, null); boolean wipeData = false; ActiveAdmin strictestAdmin = null; @@ -6051,9 +6213,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void reportSuccessfulPasswordAttempt(int userHandle) { - enforceFullCrossUsersPermission(userHandle); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.BIND_DEVICE_ADMIN, null); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); synchronized (getLockObject()) { DevicePolicyData policy = getUserData(userHandle); @@ -6079,9 +6243,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void reportFailedBiometricAttempt(int userHandle) { - enforceFullCrossUsersPermission(userHandle); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.BIND_DEVICE_ADMIN, null); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); + if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0, /*method strength*/ 0); @@ -6090,9 +6257,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void reportSuccessfulBiometricAttempt(int userHandle) { - enforceFullCrossUsersPermission(userHandle); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.BIND_DEVICE_ADMIN, null); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); + if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1, /*method strength*/ 0); @@ -6101,9 +6271,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void reportKeyguardDismissed(int userHandle) { - enforceFullCrossUsersPermission(userHandle); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.BIND_DEVICE_ADMIN, null); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISSED); @@ -6112,9 +6284,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void reportKeyguardSecured(int userHandle) { - enforceFullCrossUsersPermission(userHandle); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.BIND_DEVICE_ADMIN, null); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); if (mInjector.securityLogIsLoggingEnabled()) { SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_SECURED); @@ -6176,7 +6350,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM); // Scan through active admins and find if anyone has already @@ -6310,7 +6488,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return false; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = who != null + ? getCallerIdentity(who) + : getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { // Check for permissions if a particular caller is specified if (who != null) { @@ -6340,7 +6524,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { // Ok to return current status. } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = callerPackage != null + ? getCallerIdentity(callerPackage) + : getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); // It's not critical here, but let's make sure the package name is correct, in case // we start using it for different purposes. @@ -6485,24 +6674,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); - final int userHandle = UserHandle.getCallingUserId(); + final CallerIdentity identity = getCallerIdentity(who); + boolean requireAutoTimeChanged = false; synchronized (getLockObject()) { - if (isManagedProfile(userHandle)) { - throw new SecurityException("Managed profile cannot set auto time required"); - } - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + Preconditions.checkSecurity(!isManagedProfile(identity.getUserId()), + "Managed profile cannot set auto time required"); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); if (admin.requireAutoTime != required) { admin.requireAutoTime = required; - saveSettingsLocked(userHandle); + saveSettingsLocked(identity.getUserId()); requireAutoTimeChanged = true; } } // requireAutoTime is now backed by DISALLOW_CONFIG_DATE_TIME restriction, so propagate // updated restrictions to the framework. if (requireAutoTimeChanged) { - pushUserRestrictions(userHandle); + pushUserRestrictions(identity.getUserId()); } // Turn AUTO_TIME on in settings if it is required if (required) { @@ -7033,7 +7221,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return 0; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + final long ident = mInjector.binderClearCallingIdentity(); try { synchronized (getLockObject()) { @@ -7229,6 +7421,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return who != null && who.equals(profileOwner); } + /** + * Returns {@code true} if the provided caller identity is of a profile owner. + * @param identity identity of caller. + * @return true if {@code identity} is a profile owner, false otherwise. + */ + public boolean isProfileOwner(CallerIdentity identity) { + final ComponentName profileOwner = getProfileOwner(identity.getUserId()); + return profileOwner != null && profileOwner.equals(identity.getComponentName()); + } + private boolean hasProfileOwner(int userId) { synchronized (getLockObject()) { return mOwners.hasProfileOwner(userId); @@ -7783,7 +7985,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public ComponentName getProfileOwnerAsUser(int userHandle) { - enforceCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle)); return getProfileOwner(userHandle); } @@ -8140,56 +8345,31 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private void enforceAcrossUsersPermissions() { - final int callingUid = mInjector.binderGetCallingUid(); - final int callingPid = mInjector.binderGetCallingPid(); - final String packageName = mContext.getPackageName(); - - if (isCallerWithSystemUid() || callingUid == Process.ROOT_UID) { - return; - } - if (PermissionChecker.checkPermissionForPreflight( - mContext, permission.INTERACT_ACROSS_PROFILES, callingPid, callingUid, - packageName) == PermissionChecker.PERMISSION_GRANTED) { - return; - } - if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS) - == PackageManager.PERMISSION_GRANTED) { - return; - } - if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS_FULL) - == PackageManager.PERMISSION_GRANTED) { - return; - } - throw new SecurityException("Calling user does not have INTERACT_ACROSS_PROFILES or" - + "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL permissions"); + private boolean hasCallingPermission(String permission) { + return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED; } - private void enforceFullCrossUsersPermission(int userHandle) { - enforceSystemUserOrPermissionIfCrossUser(userHandle, - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + private boolean hasCallingOrSelfPermission(String permission) { + return mContext.checkCallingOrSelfPermission(permission) + == PackageManager.PERMISSION_GRANTED; } - private void enforceCrossUsersPermission(int userHandle) { - enforceSystemUserOrPermissionIfCrossUser(userHandle, - android.Manifest.permission.INTERACT_ACROSS_USERS); + private boolean hasPermissionForPreflight(CallerIdentity identity, String permission) { + final int callingPid = mInjector.binderGetCallingPid(); + final String packageName = mContext.getPackageName(); + + return PermissionChecker.checkPermissionForPreflight(mContext, permission, callingPid, + identity.getUid(), packageName) == PermissionChecker.PERMISSION_GRANTED; } - private void enforceSystemUserOrPermission(String permission) { - if (!(isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID)) { - mContext.enforceCallingOrSelfPermission(permission, - "Must be system or have " + permission + " permission"); - } + private boolean hasFullCrossUsersPermission(CallerIdentity identity, int userHandle) { + return (userHandle == identity.getUserId()) || isSystemUid(identity) || isRootUid(identity) + || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL); } - private void enforceSystemUserOrPermissionIfCrossUser(int userHandle, String permission) { - if (userHandle < 0) { - throw new IllegalArgumentException("Invalid userId " + userHandle); - } - if (userHandle == mInjector.userHandleGetCallingUserId()) { - return; - } - enforceSystemUserOrPermission(permission); + private boolean hasCrossUsersPermission(CallerIdentity identity, int userHandle) { + return (userHandle == identity.getUserId()) || isSystemUid(identity) || isRootUid(identity) + || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS); } private void enforceManagedProfile(int userId, String message) { @@ -8249,19 +8429,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new SecurityException("No active admin found"); } - private void enforceProfileOwnerOrFullCrossUsersPermission(int userId) { - if (userId == mInjector.userHandleGetCallingUserId()) { + private void enforceProfileOwnerOrFullCrossUsersPermission(CallerIdentity identity, + int userId) { + if (userId == identity.getUserId()) { synchronized (getLockObject()) { if (getActiveAdminWithPolicyForUidLocked(null, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid()) - != null) { + DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, identity.getUid()) != null) { // Device Owner/Profile Owner may access the user it runs on. return; } } } - // Otherwise, INTERACT_ACROSS_USERS_FULL permission, system UID or root UID is required. - enforceSystemUserOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId)); } private boolean canUserUseLockTaskLocked(int userId) { @@ -8315,6 +8494,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID); } + private boolean isSystemUid(CallerIdentity identity) { + return UserHandle.isSameApp(identity.getUid(), Process.SYSTEM_UID); + } + + private boolean isRootUid(CallerIdentity identity) { + return UserHandle.isSameApp(identity.getUid(), Process.ROOT_UID); + } + + private boolean isShellUid(CallerIdentity identity) { + return UserHandle.isSameApp(identity.getUid(), Process.SHELL_UID); + } + protected int getProfileParentId(int userHandle) { return mInjector.binderWithCleanCallingIdentity(() -> { UserInfo parentUser = mUserManager.getProfileParent(userHandle); @@ -8569,7 +8760,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(agent, "agent null"); - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = admin != null + ? getCallerIdentity(admin) + : getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); synchronized (getLockObject()) { final String componentName = agent.flattenToString(); @@ -8769,9 +8965,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); if (packageList != null) { - int userId = UserHandle.getCallingUserId(); + int userId = identity.getUserId(); List<AccessibilityServiceInfo> enabledServices = null; long id = mInjector.binderClearCallingIdentity(); try { @@ -8801,8 +8998,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); admin.permittedAccessiblityServices = packageList; saveSettingsLocked(UserHandle.getCallingUserId()); } @@ -8822,10 +9018,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity)); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); return admin.permittedAccessiblityServices; } } @@ -8920,17 +9117,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(who, "ComponentName is null"); - final int callingUserId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity)); + if (packageList != null) { List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get() - .getEnabledInputMethodListAsUser(callingUserId); + .getEnabledInputMethodListAsUser(identity.getUserId()); if (enabledImes != null) { List<String> enabledPackages = new ArrayList<String>(); for (InputMethodInfo ime : enabledImes) { enabledPackages.add(ime.getPackageName()); } if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList, - callingUserId)) { + identity.getUserId())) { Slog.e(LOG_TAG, "Cannot set permitted input methods, " + "because it contains already enabled input method."); return false; @@ -8939,10 +9138,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); admin.permittedInputMethods = packageList; - saveSettingsLocked(callingUserId); + saveSettingsLocked(identity.getUserId()); } final String[] packageArray = packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null; @@ -8960,10 +9158,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity)); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); return admin.permittedInputMethods; } } @@ -9037,17 +9236,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); - final int callingUserId = mInjector.userHandleGetCallingUserId(); - if (!isManagedProfile(callingUserId)) { + if (!isManagedProfile(identity.getUserId())) { return false; } synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); admin.permittedNotificationListeners = packageList; - saveSettingsLocked(callingUserId); + saveSettingsLocked(identity.getUserId()); } return true; } @@ -9058,10 +9256,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + // API contract is to return null if there are no permitted cross-profile notification + // listeners, including in Device Owner mode. + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); return admin.permittedNotificationListeners; } } @@ -9922,10 +10122,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public String[] getAccountTypesWithManagementDisabledAsUser(int userId, boolean parent) { - enforceFullCrossUsersPermission(userId); if (!mHasFeature) { return null; } + Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId)); + synchronized (getLockObject()) { final ArraySet<String> resultSet = new ArraySet<>(); @@ -9992,10 +10196,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = UserHandle.getCallingUserId(); synchronized (getLockObject()) { + //TODO: This is a silly access control check. Remove. if (who != null) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDeviceOwner(caller)); } - long id = mInjector.binderClearCallingIdentity(); try { return mIPackageManager.getBlockUninstallForUser(packageName, userId); @@ -10015,12 +10221,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(identity)); + synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); if (admin.disableCallerId != disabled) { admin.disableCallerId = disabled; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + saveSettingsLocked(identity.getUserId()); } } DevicePolicyEventLogger @@ -10036,16 +10244,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(identity)); + synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); return admin.disableCallerId; } } @Override public boolean getCrossProfileCallerIdDisabledForUser(int userId) { - enforceCrossUsersPermission(userId); + Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userId)); + synchronized (getLockObject()) { ActiveAdmin admin = getProfileOwnerAdminLocked(userId); return (admin != null) ? admin.disableCallerId : false; @@ -10058,12 +10272,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(identity)); + synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); if (admin.disableContactsSearch != disabled) { admin.disableContactsSearch = disabled; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + saveSettingsLocked(identity.getUserId()); } } DevicePolicyEventLogger @@ -10079,16 +10295,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(identity)); + synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); return admin.disableContactsSearch; } } @Override public boolean getCrossProfileContactsSearchDisabledForUser(int userId) { - enforceCrossUsersPermission(userId); + Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userId)); + synchronized (getLockObject()) { ActiveAdmin admin = getProfileOwnerAdminLocked(userId); return (admin != null) ? admin.disableContactsSearch : false; @@ -10159,12 +10381,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity)); + synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); if (admin.disableBluetoothContactSharing != disabled) { admin.disableBluetoothContactSharing = disabled; - saveSettingsLocked(UserHandle.getCallingUserId()); + saveSettingsLocked(identity.getUserId()); } } DevicePolicyEventLogger @@ -10180,9 +10404,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(identity) || isProfileOwner(identity)); + synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); return admin.disableBluetoothContactSharing; } } @@ -12138,13 +12364,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); - final int userHandle = mInjector.userHandleGetCallingUserId(); - enforceManagedProfile(userHandle, "set organization color"); + final CallerIdentity identity = getCallerIdentity(who); + enforceManagedProfile(identity.getUserId(), "set organization color"); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); admin.organizationColor = color; - saveSettingsLocked(userHandle); + saveSettingsLocked(identity.getUserId()); } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_ORGANIZATION_COLOR) @@ -12157,7 +12382,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return; } - enforceFullCrossUsersPermission(userId); + Preconditions.checkArgumentNonnegative(userId, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId)); + enforceManageUsers(); enforceManagedProfile(userId, "set organization color"); synchronized (getLockObject()) { @@ -12173,10 +12402,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return ActiveAdmin.DEF_ORGANIZATION_COLOR; } Objects.requireNonNull(who, "ComponentName is null"); - enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "get organization color"); + final CallerIdentity identity = getCallerIdentity(who); + enforceManagedProfile(identity.getUserId(), "get organization color"); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); return admin.organizationColor; } } @@ -12186,7 +12415,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return ActiveAdmin.DEF_ORGANIZATION_COLOR; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + enforceManagedProfile(userHandle, "get organization color"); synchronized (getLockObject()) { ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle); @@ -12202,15 +12435,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); - final int userHandle = mInjector.userHandleGetCallingUserId(); + final CallerIdentity identity = getCallerIdentity(who); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); if (!TextUtils.equals(admin.organizationName, text)) { admin.organizationName = (text == null || text.length() == 0) ? null : text.toString(); - saveSettingsLocked(userHandle); + saveSettingsLocked(identity.getUserId()); } } } @@ -12221,10 +12453,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } Objects.requireNonNull(who, "ComponentName is null"); - enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "get organization name"); + final CallerIdentity identity = getCallerIdentity(who); + enforceManagedProfile(identity.getUserId(), "get organization name"); synchronized (getLockObject()) { - ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); return admin.organizationName; } } @@ -12246,7 +12478,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return null; } - enforceFullCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle)); + enforceManagedProfile(userHandle, "get organization name"); synchronized (getLockObject()) { ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle); @@ -12260,20 +12496,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public List<String> setMeteredDataDisabledPackages(ComponentName who, List<String> packageNames) { Objects.requireNonNull(who); Objects.requireNonNull(packageNames); + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkSecurity(isDeviceOwner(identity) || isProfileOwner(identity), + String.format("Admin %s does not own the profile", identity.getComponentName())); if (!mHasFeature) { return packageNames; } synchronized (getLockObject()) { - final ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - final int callingUserId = mInjector.userHandleGetCallingUserId(); + final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); return mInjector.binderWithCleanCallingIdentity(() -> { - final List<String> excludedPkgs - = removeInvalidPkgsForMeteredDataRestriction(callingUserId, packageNames); + final List<String> excludedPkgs = removeInvalidPkgsForMeteredDataRestriction( + identity.getUserId(), packageNames); admin.meteredDisabledPackages = packageNames; - pushMeteredDisabledPackagesLocked(callingUserId); - saveSettingsLocked(callingUserId); + pushMeteredDisabledPackagesLocked(identity.getUserId()); + saveSettingsLocked(identity.getUserId()); return excludedPkgs; }); } @@ -12310,9 +12547,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return new ArrayList<>(); } + final CallerIdentity identity = getCallerIdentity(who); + Preconditions.checkSecurity(isDeviceOwner(identity) || isProfileOwner(identity), + String.format("Admin %s does not own the profile", identity.getComponentName())); + synchronized (getLockObject()) { - final ActiveAdmin admin = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity); return admin.meteredDisabledPackages == null ? new ArrayList<>() : admin.meteredDisabledPackages; } @@ -12337,12 +12577,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } - private boolean hasMarkProfileOwnerOnOrganizationOwnedDevicePermission() { - return mContext.checkCallingPermission( - permission.MARK_DEVICE_ORGANIZATION_OWNED) - == PackageManager.PERMISSION_GRANTED; - } - @Override public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) { // As the caller is the system, it must specify the component name of the profile owner @@ -12355,7 +12589,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Only adb or system apps with the right permission can mark a profile owner on // organization-owned device. - if (!(isAdb() || hasMarkProfileOwnerOnOrganizationOwnedDevicePermission())) { + if (!(isAdb() || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) { throw new SecurityException( "Only the system can mark a profile owner of organization-owned device."); } @@ -13483,7 +13717,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public StringParceledListSlice getOwnerInstalledCaCerts(@NonNull UserHandle user) { final int userId = user.getIdentifier(); - enforceProfileOwnerOrFullCrossUsersPermission(userId); + final CallerIdentity identity = getCallerIdentity(); + enforceProfileOwnerOrFullCrossUsersPermission(identity, userId); synchronized (getLockObject()) { return new StringParceledListSlice( new ArrayList<>(getUserData(userId).mOwnerInstalledCaCerts)); @@ -14100,12 +14335,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return; } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); synchronized (getLockObject()) { - final ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); admin.mCrossProfileCalendarPackages = packageNames; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + saveSettingsLocked(identity.getUserId()); } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_CALENDAR_PACKAGES) @@ -14121,10 +14356,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return Collections.emptyList(); } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); synchronized (getLockObject()) { - final ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); return admin.mCrossProfileCalendarPackages; } } @@ -14136,8 +14371,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty"); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle)); - enforceCrossUsersPermission(userHandle); synchronized (getLockObject()) { if (mInjector.settingsSecureGetIntForUser( Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, 0, userHandle) == 0) { @@ -14159,7 +14397,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return Collections.emptyList(); } - enforceCrossUsersPermission(userHandle); + Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); + + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle)); + synchronized (getLockObject()) { final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle); if (admin != null) { @@ -14176,16 +14418,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(packageNames, "Package names is null"); + final CallerIdentity identity = getCallerIdentity(who); + final List<String> previousCrossProfilePackages; synchronized (getLockObject()) { - final ActiveAdmin admin = - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); previousCrossProfilePackages = admin.mCrossProfilePackages; if (packageNames.equals(previousCrossProfilePackages)) { return; } admin.mCrossProfilePackages = packageNames; - saveSettingsLocked(mInjector.userHandleGetCallingUserId()); + saveSettingsLocked(identity.getUserId()); } logSetCrossProfilePackages(who, packageNames); final CrossProfileApps crossProfileApps = mContext.getSystemService(CrossProfileApps.class); @@ -14208,10 +14451,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return Collections.emptyList(); } Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity identity = getCallerIdentity(who); synchronized (getLockObject()) { - final ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity); return admin.mCrossProfilePackages; } } @@ -14221,7 +14464,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature) { return Collections.emptyList(); } - enforceAcrossUsersPermissions(); + final CallerIdentity identity = getCallerIdentity(); + Preconditions.checkCallAuthorization( + isSystemUid(identity) || isRootUid(identity) || hasCallingPermission( + permission.INTERACT_ACROSS_USERS) || hasCallingPermission( + permission.INTERACT_ACROSS_USERS_FULL) || hasPermissionForPreflight( + identity, permission.INTERACT_ACROSS_PROFILES)); synchronized (getLockObject()) { final List<ActiveAdmin> admins = getProfileOwnerAdminsForCurrentProfileGroup(); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 32afe8244eb6..e6fc792c6a9d 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4465,6 +4465,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436); addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1); mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; + mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL); // Even if the caller is the managed profile, the current user is the user 0 when(getServices().iactivityManager.getCurrentUser()) @@ -5694,6 +5695,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { final long ident = mServiceContext.binder.clearCallingIdentity(); configureContextForAccess(mServiceContext, true); + mServiceContext.permissions.add(permission.MARK_DEVICE_ORGANIZATION_OWNED); mServiceContext.binder.callingUid = UserHandle.getUid(CALLER_USER_HANDLE, diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index ce7ac9e796d2..09b6d7b0cd7e 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -259,18 +259,7 @@ public class DpmMockContext extends MockContext { @Override public int checkPermission(String permission, int pid, int uid) { - if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) { - return PackageManager.PERMISSION_GRANTED; // Assume system has all permissions. - } - List<String> permissions = binder.callingPermissions.get(binder.getCallingUid()); - if (permissions == null) { - permissions = callerPermissions; - } - if (permissions.contains(permission)) { - return PackageManager.PERMISSION_GRANTED; - } else { - return PackageManager.PERMISSION_DENIED; - } + return checkPermission(permission); } @Override @@ -480,11 +469,32 @@ public class DpmMockContext extends MockContext { @Override public int checkCallingPermission(String permission) { - return spiedContext.checkCallingPermission(permission); + return checkPermission(permission); + } + + @Override + public int checkCallingOrSelfPermission(String permission) { + return checkPermission(permission); } @Override public void startActivityAsUser(Intent intent, UserHandle userHandle) { spiedContext.startActivityAsUser(intent, userHandle); } + + private int checkPermission(String permission) { + if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) { + return PackageManager.PERMISSION_GRANTED; // Assume system has all permissions. + } + List<String> permissions = binder.callingPermissions.get(binder.getCallingUid()); + if (permissions == null) { + permissions = callerPermissions; + } + if (permissions.contains(permission)) { + return PackageManager.PERMISSION_GRANTED; + } else { + return PackageManager.PERMISSION_DENIED; + } + } + } diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt index 946f27e09fdb..d36dcce800eb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt @@ -20,7 +20,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageParser -import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.Postsubmit import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.appInfo import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.pkgInfo import com.android.server.pm.parsing.pkg.AndroidPackage @@ -38,7 +38,7 @@ import org.junit.runners.Parameterized * This test has to be updated manually whenever the info generation behavior changes, since * there's no single place where flag -> field is defined besides this test. */ -@Presubmit +@Postsubmit @RunWith(Parameterized::class) class AndroidPackageInfoFlagBehaviorTest : AndroidPackageParsingTestBase() { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt index f96ebda67602..574921cdbd05 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt @@ -17,26 +17,20 @@ package com.android.server.pm.parsing import android.content.pm.PackageManager -import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.Postsubmit import androidx.test.filters.LargeTest import com.google.common.truth.Expect - import org.junit.Rule import org.junit.Test -import org.junit.rules.Timeout -import java.util.concurrent.TimeUnit /** * Collects APKs from the device and verifies that the new parsing behavior outputs * the same exposed Info object as the old parsing logic. */ -@Presubmit +@Postsubmit class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { @get:Rule - val timeout = Timeout(4, TimeUnit.MINUTES) - - @get:Rule val expect = Expect.create() @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index 555906d4c910..608305c33168 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -43,7 +43,6 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; import android.platform.test.annotations.Presubmit; -import android.util.IntArray; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.test.InsetsModeSession; @@ -242,8 +241,7 @@ public class InsetsPolicyTest extends WindowTestsBase { }).when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -271,8 +269,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -301,8 +298,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -340,8 +336,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(app); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(app); policy.updateBarControlTarget(app2); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 085230d35c6a..59f8cc8c3412 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -346,8 +346,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); provider.getSource().setVisible(false); - mDisplayContent.getInsetsPolicy().showTransient( - IntArray.wrap(new int[] { ITYPE_STATUS_BAR })); + mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR }); assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR)); assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index fc54e1de888f..0fe6510b0fce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -50,6 +50,7 @@ import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -245,17 +246,17 @@ public class TaskRecordTests extends WindowTestsBase { final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); final DisplayContent display = new TestDisplayContent.Builder(mAtm, fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); - assertTrue(mRootWindowContainer.getDisplayContent(display.mDisplayId) != null); + assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); // Fix the display orientation to landscape which is the natural rotation (0) for the test // display. final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); - Task stack = new StackBuilder(mRootWindowContainer) + final Task stack = new StackBuilder(mRootWindowContainer) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); - Task task = stack.getBottomMostTask(); - ActivityRecord root = task.getTopNonFinishingActivity(); + final Task task = stack.getBottomMostTask(); + final ActivityRecord root = task.getTopNonFinishingActivity(); assertEquals(fullScreenBounds, task.getBounds()); @@ -267,7 +268,7 @@ public class TaskRecordTests extends WindowTestsBase { assertEquals(fullScreenBounds.height(), task.getBounds().height()); // Top activity gets used - ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setStack(stack).build(); + final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setStack(stack).build(); assertEquals(top, task.getTopNonFinishingActivity()); top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height()); @@ -304,6 +305,33 @@ public class TaskRecordTests extends WindowTestsBase { } @Test + public void testReportsOrientationRequestInLetterboxForOrientation() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); + final DisplayContent display = new TestDisplayContent.Builder(mAtm, + fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); + assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); + // Fix the display orientation to landscape which is the natural rotation (0) for the test + // display. + final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); + dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); + + final Task stack = new StackBuilder(mRootWindowContainer) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); + final Task task = stack.getBottomMostTask(); + ActivityRecord root = task.getTopNonFinishingActivity(); + + assertEquals(fullScreenBounds, task.getBounds()); + + // Setting app to fixed portrait fits within parent + root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + assertThat(task.getBounds().width()).isLessThan(task.getBounds().height()); + + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation()); + } + + @Test public void testIgnoresForcedOrientationWhenParentHandles() { final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); DisplayContent display = new TestDisplayContent.Builder( diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index 6ed762283524..78dfd407ff4e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -55,6 +55,18 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { } @Test + public void testSkipResume() { + final ActivityRecord activity = createTestActivityRecord(mDisplayContent); + activity.mLaunchTaskBehind = true; + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(activity); + + // Make sure our handler processed the message. + waitHandlerIdle(mWm.mH); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); + } + + @Test public void testMultiple() { final ActivityRecord activity1 = createTestActivityRecord(mDisplayContent); final ActivityRecord activity2 = createTestActivityRecord(mDisplayContent); |