diff options
141 files changed, 3220 insertions, 1932 deletions
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 99e82e7a3367..579963b71cf7 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -94,7 +94,6 @@ stubs_defaults { // TODO(b/135922046) remove this include_dirs: ["frameworks/base/core/java"], }, - sdk_version: "system_current", } droidstubs { diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp index 793247e88614..fc9052e8beac 100644 --- a/apex/permission/framework/Android.bp +++ b/apex/permission/framework/Android.bp @@ -46,12 +46,10 @@ stubs_defaults { name: "framework-permission-stubs-defaults", srcs: [ ":framework-permission-sources" ], libs: [ "framework-annotations-lib" ], - sdk_version: "system_current", } droidstubs { name: "framework-permission-stubs-srcs-publicapi", - sdk_version: "system_current", defaults: [ "framework-module-stubs-defaults-publicapi", "framework-permission-stubs-defaults", @@ -60,7 +58,6 @@ droidstubs { droidstubs { name: "framework-permission-stubs-srcs-systemapi", - sdk_version: "system_current", defaults: [ "framework-module-stubs-defaults-systemapi", "framework-permission-stubs-defaults", @@ -69,7 +66,6 @@ droidstubs { droidstubs { name: "framework-permission-api-module_libs_api", - sdk_version: "system_current", defaults: [ "framework-module-api-defaults-module_libs_api", "framework-permission-stubs-defaults", @@ -78,7 +74,6 @@ droidstubs { droidstubs { name: "framework-permission-stubs-srcs-module_libs_api", - sdk_version: "system_current", defaults: [ "framework-module-stubs-defaults-module_libs_api", "framework-permission-stubs-defaults", diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp index 707113b9672c..3eabb88fec7d 100644 --- a/apex/sdkextensions/framework/Android.bp +++ b/apex/sdkextensions/framework/Android.bp @@ -48,7 +48,6 @@ stubs_defaults { name: "framework-sdkextensions-stubs-defaults", srcs: [ ":framework-sdkextensions-sources" ], libs: [ "framework-annotations-lib" ], - sdk_version: "system_current", } droidstubs { diff --git a/api/test-current.txt b/api/test-current.txt index c29f1c4c657d..641767c7bccd 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -214,6 +214,7 @@ package android.app { field public static final String OPSTR_GPS = "android:gps"; field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground"; field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; + field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage"; field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels"; field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone"; field public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells"; @@ -5215,10 +5216,10 @@ package android.window { method public void onDisplayAreaAppeared(@NonNull android.window.WindowContainerToken); method public void onDisplayAreaVanished(@NonNull android.window.WindowContainerToken); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void registerOrganizer(int); + field public static final int FEATURE_DEFAULT_TASK_CONTAINER = 1; // 0x1 field public static final int FEATURE_ROOT = 0; // 0x0 field public static final int FEATURE_SYSTEM_FIRST = 0; // 0x0 field public static final int FEATURE_SYSTEM_LAST = 10000; // 0x2710 - field public static final int FEATURE_TASK_CONTAINER = 1; // 0x1 field public static final int FEATURE_UNDEFINED = -1; // 0xffffffff field public static final int FEATURE_VENDOR_FIRST = 10001; // 0x2711 field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2 diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp index 28bf21ae52bf..743ccc4ed451 100644 --- a/cmds/statsd/benchmark/filter_value_benchmark.cpp +++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp @@ -14,12 +14,14 @@ * limitations under the License. */ #include <vector> -#include "benchmark/benchmark.h" + #include "FieldValue.h" #include "HashableDimensionKey.h" +#include "benchmark/benchmark.h" #include "logd/LogEvent.h" -#include "stats_log_util.h" +#include "metric_util.h" #include "stats_event.h" +#include "stats_log_util.h" namespace android { namespace os { @@ -34,24 +36,13 @@ static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matche std::vector<int> attributionUids = {100, 100}; std::vector<string> attributionTags = {"LOCATION", "LOCATION"}; + writeAttribution(statsEvent, attributionUids, attributionTags); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); AStatsEvent_writeFloat(statsEvent, 3.2f); AStatsEvent_writeString(statsEvent, "LOCATION"); AStatsEvent_writeInt64(statsEvent, 990); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - event->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, event); field_matcher->set_field(1); auto child = field_matcher->add_child(); diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp index c7d01cc406fc..7a455650a31b 100644 --- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp +++ b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp @@ -14,12 +14,14 @@ * limitations under the License. */ #include <vector> -#include "benchmark/benchmark.h" + #include "FieldValue.h" #include "HashableDimensionKey.h" +#include "benchmark/benchmark.h" #include "logd/LogEvent.h" -#include "stats_log_util.h" +#include "metric_util.h" #include "stats_event.h" +#include "stats_log_util.h" namespace android { namespace os { @@ -34,24 +36,13 @@ static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) { std::vector<int> attributionUids = {100, 100}; std::vector<string> attributionTags = {"LOCATION", "LOCATION"}; + writeAttribution(statsEvent, attributionUids, attributionTags); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); AStatsEvent_writeFloat(statsEvent, 3.2f); AStatsEvent_writeString(statsEvent, "LOCATION"); AStatsEvent_writeInt64(statsEvent, 990); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - event->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, event); link->conditionId = 1; diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp index 482d66fc7556..89fd3d9b29ab 100644 --- a/cmds/statsd/benchmark/metric_util.cpp +++ b/cmds/statsd/benchmark/metric_util.cpp @@ -247,21 +247,37 @@ FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) return dimensions; } +void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids, + const vector<string>& attributionTags) { + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } + + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); +} + +void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) { + AStatsEvent_build(statsEvent); + + size_t size; + uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); + logEvent->parseBuffer(buf, size); + + AStatsEvent_release(statsEvent); +} + std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( uint64_t timestampNs, const android::view::DisplayStateEnum state) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -272,24 +288,12 @@ std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent( AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeString(statsEvent, jobName.c_str()); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -319,24 +323,12 @@ std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs, AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeString(statsEvent, name.c_str()); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h index c5fcf7c27440..3efaa850a921 100644 --- a/cmds/statsd/benchmark/metric_util.h +++ b/cmds/statsd/benchmark/metric_util.h @@ -18,6 +18,7 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "src/StatsLogProcessor.h" #include "src/logd/LogEvent.h" +#include "stats_event.h" #include "statslog.h" namespace android { @@ -92,6 +93,11 @@ FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, FieldMatcher CreateAttributionUidDimensions(const int atomId, const std::vector<Position>& positions); +void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids, + const vector<string>& attributionTags); + +void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent); + // Create log event for screen state changed. std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( uint64_t timestampNs, const android::view::DisplayStateEnum state); diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp index 4385964f7f0e..cfc1de49e259 100644 --- a/cmds/statsd/src/FieldValue.cpp +++ b/cmds/statsd/src/FieldValue.cpp @@ -120,9 +120,9 @@ bool isAttributionUidField(const FieldValue& value) { } int32_t getUidIfExists(const FieldValue& value) { - // the field is uid field if the field is the uid field in attribution node or marked as - // is_uid in atoms.proto - bool isUid = isAttributionUidField(value) || isUidField(value.mField, value.mValue); + // the field is uid field if the field is the uid field in attribution node + // or annotated as such in the atom + bool isUid = isAttributionUidField(value) || isUidField(value); return isUid ? value.mValue.int_value : -1; } @@ -134,16 +134,8 @@ bool isAttributionUidField(const Field& field, const Value& value) { return false; } -bool isUidField(const Field& field, const Value& value) { - auto it = android::util::AtomsInfo::kAtomsWithUidField.find(field.getTag()); - - if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) { - int uidField = it->second; // uidField is the field number in proto - return field.getDepth() == 0 && field.getPosAtDepth(0) == uidField && - value.getType() == INT; - } - - return false; +bool isUidField(const FieldValue& fieldValue) { + return fieldValue.mAnnotations.isUidField(); } Value::Value(const Value& from) { diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h index 3536e5a5c962..92e09ea0f8f9 100644 --- a/cmds/statsd/src/FieldValue.h +++ b/cmds/statsd/src/FieldValue.h @@ -367,7 +367,8 @@ public: enum { NESTED_POS = 0x0, PRIMARY_POS = 0x1, - EXCLUSIVE_POS = 0x2 + EXCLUSIVE_POS = 0x2, + UID_POS = 0x3 }; inline void setNested(bool nested) { setBitmaskAtPos(NESTED_POS, nested); } @@ -376,6 +377,8 @@ public: inline void setExclusiveState(bool exclusive) { setBitmaskAtPos(EXCLUSIVE_POS, exclusive); } + inline void setUidField(bool isUid) { setBitmaskAtPos(UID_POS, isUid); } + inline void setResetState(int resetState) { mResetState = resetState; } // Default value = false @@ -387,6 +390,9 @@ public: // Default value = false inline bool isExclusiveState() const { return getValueFromBitmask(EXCLUSIVE_POS); } + // Default value = false + inline bool isUidField() const { return getValueFromBitmask(UID_POS); } + // If a reset state is not sent in the StatsEvent, returns -1. Note that a // reset satate is only sent if and only if a reset should be triggered. inline int getResetState() const { return mResetState; } @@ -402,7 +408,7 @@ private: } // This is a bitmask over all annotations stored in boolean form. Because - // there are only 3 booleans, just one byte is required. + // there are only 4 booleans, just one byte is required. uint8_t mBooleanBitmask = 0; int mResetState = -1; @@ -449,7 +455,7 @@ int getUidIfExists(const FieldValue& value); void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output); bool isAttributionUidField(const Field& field, const Value& value); -bool isUidField(const Field& field, const Value& value); +bool isUidField(const FieldValue& fieldValue); bool equalDimensions(const std::vector<Matcher>& dimension_a, const std::vector<Matcher>& dimension_b); diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 982a63e3e08c..325cbc7e80e5 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -138,13 +138,6 @@ void StatsLogProcessor::onPeriodicAlarmFired( } } -void updateUid(Value* value, int hostUid) { - int uid = value->int_value; - if (uid != hostUid) { - value->setInt(hostUid); - } -} - void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event) const { if (android::util::AtomsInfo::kAtomsWithAttributionChain.find(event->GetTagId()) != android::util::AtomsInfo::kAtomsWithAttributionChain.end()) { @@ -154,22 +147,15 @@ void StatsLogProcessor::mapIsolatedUidToHostUidIfNecessaryLocked(LogEvent* event } if (isAttributionUidField(value)) { const int hostUid = mUidMap->getHostUidOrSelf(value.mValue.int_value); - updateUid(&value.mValue, hostUid); + value.mValue.setInt(hostUid); } } } else { - auto it = android::util::AtomsInfo::kAtomsWithUidField.find(event->GetTagId()); - if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) { - int uidField = it->second; // uidField is the field number in proto, - // starting from 1 - if (uidField > 0 && (int)event->getValues().size() >= uidField && - (event->getValues())[uidField - 1].mValue.getType() == INT) { - Value& value = (*event->getMutableValues())[uidField - 1].mValue; - const int hostUid = mUidMap->getHostUidOrSelf(value.int_value); - updateUid(&value, hostUid); - } else { - ALOGE("Malformed log, uid not found. %s", event->ToString().c_str()); - } + int uidFieldIndex = event->getUidFieldIndex(); + if (uidFieldIndex != -1) { + Value& value = (*event->getMutableValues())[uidFieldIndex].mValue; + const int hostUid = mUidMap->getHostUidOrSelf(value.int_value); + value.setInt(hostUid); } } } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index bd15264c008f..2a1716e0ce2a 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -418,7 +418,8 @@ message Atom { AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"]; SharesheetStarted sharesheet_started = 259 [(module) = "framework"]; RankingSelected ranking_selected = 260 [(module) = "framework"]; - TvSettingsUIInteracted tvsettings_ui_interacted = 261; + TvSettingsUIInteracted tvsettings_ui_interacted = 261 [(module) = "tv_settings"]; + LauncherStaticLayout launcher_snapshot = 262 [(module) = "sysui"]; SdkExtensionStatus sdk_extension_status = 354; } @@ -2979,14 +2980,98 @@ message ExclusionRectStateChanged { optional int32 duration_millis = 7; } +/** + * Logs when Launcher (HomeScreen) UI has changed or was interacted. + * + * Logged from: + * packages/apps/Launcher3 + */ message LauncherUIChanged { - optional android.stats.launcher.LauncherAction action = 1; + optional android.stats.launcher.LauncherAction action = 1 [deprecated = true]; optional android.stats.launcher.LauncherState src_state = 2; optional android.stats.launcher.LauncherState dst_state = 3; - optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES]; - optional bool is_swipe_up_enabled = 5; + optional android.stats.launcher.LauncherExtension extension = 4 [(log_mode) = MODE_BYTES, deprecated = true]; + optional bool is_swipe_up_enabled = 5 [deprecated = true]; + + // The event id (e.g., app launch, drag and drop, long press) + optional int32 event_id = 6; + // The event's source or target id (e.g., icon, task, button) + optional int32 target_id = 7; + // If the target needs to be tracked, use this id field + optional int32 instance_id = 8; + optional int32 uid = 9 [(is_uid) = true]; + optional string package_name = 10; + optional string component_name = 11; + + // (x, y) coordinate and the index information of the target on the container + optional int32 grid_x = 12; + optional int32 grid_y = 13; + optional int32 page_id = 14; + + // e.g., folder icon's (x, y) location and index information on the workspace + optional int32 grid_x_parent = 15; + optional int32 grid_y_parent = 16; + optional int32 page_id_parent = 17; + + // e.g., SEARCHBOX_ALLAPPS, FOLDER_WORKSPACE + optional int32 hierarchy = 18; + + optional bool is_work_profile = 19; + + // Used to store the predicted rank of the target + optional int32 rank = 20; + + // e.g., folderLabelState can be captured in the following two fields + optional int32 from_state = 21; + optional int32 to_state = 22; + + // e.g., autofilled or suggested texts that are not user entered + optional string edittext = 23; } +/** + * Used for snapshot of the HomeScreen UI elements + * + * Logged from: + * packages/apps/Launcher3 + */ +message LauncherStaticLayout { + // The event id (e.g., snapshot, drag and drop) + optional int32 event_id = 1; + // The event's source or target id (e.g., icon, shortcut, widget) + optional int32 target_id = 2; + // If the target needs to be tracked, use this id field + optional int32 instance_id = 3; + optional int32 uid = 4 [(is_uid) = true]; + optional string package_name = 5; + optional string component_name = 6; + + // (x, y) coordinate and the index information of the target on the container + optional int32 grid_x = 7; + optional int32 grid_y = 8; + optional int32 page_id = 9; + + // e.g., folder icon's (x, y) location and index information on the workspace + optional int32 grid_x_parent = 10; + optional int32 grid_y_parent = 11; + optional int32 page_id_parent = 12; + + // e.g., WORKSPACE, HOTSEAT, FOLDER_WORKSPACE, FOLDER_HOTSEAT + optional int32 hierarchy = 13; + + optional bool is_work_profile = 14; + + // e.g., PIN, WIDGET TRAY, APPS TRAY, PREDICTION + optional int32 origin = 15; +} + +/** + * Logs when Wallpaper or ThemePicker UI has changed. + * + * Logged from: + * packages/apps/ThemePicker + * packages/apps/WallpaperPicker2 + */ message StyleUIChanged { optional android.stats.style.Action action = 1; optional int32 color_package_hash = 2; diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 6d9c644bb40e..bbae3fef7934 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -43,20 +43,23 @@ using android::base::StringPrintf; using std::unique_ptr; struct ConfigReceiverDeathCookie { - ConfigReceiverDeathCookie(sp<ConfigManager> configManager, const ConfigKey& configKey, - const shared_ptr<IPendingIntentRef>& pir): - mConfigManager(configManager), - mConfigKey(configKey), - mPir(pir) {} + ConfigReceiverDeathCookie(const wp<ConfigManager>& configManager, const ConfigKey& configKey, + const shared_ptr<IPendingIntentRef>& pir) : + mConfigManager(configManager), mConfigKey(configKey), mPir(pir) { + } - sp<ConfigManager> mConfigManager; + wp<ConfigManager> mConfigManager; ConfigKey mConfigKey; shared_ptr<IPendingIntentRef> mPir; }; void ConfigManager::configReceiverDied(void* cookie) { auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie); - sp<ConfigManager>& thiz = cookie_->mConfigManager; + sp<ConfigManager> thiz = cookie_->mConfigManager.promote(); + if (!thiz) { + return; + } + ConfigKey& configKey = cookie_->mConfigKey; shared_ptr<IPendingIntentRef>& pir = cookie_->mPir; @@ -74,20 +77,23 @@ void ConfigManager::configReceiverDied(void* cookie) { } struct ActiveConfigChangedReceiverDeathCookie { - ActiveConfigChangedReceiverDeathCookie(sp<ConfigManager> configManager, const int uid, - const shared_ptr<IPendingIntentRef>& pir): - mConfigManager(configManager), - mUid(uid), - mPir(pir) {} + ActiveConfigChangedReceiverDeathCookie(const wp<ConfigManager>& configManager, const int uid, + const shared_ptr<IPendingIntentRef>& pir) : + mConfigManager(configManager), mUid(uid), mPir(pir) { + } - sp<ConfigManager> mConfigManager; + wp<ConfigManager> mConfigManager; int mUid; shared_ptr<IPendingIntentRef> mPir; }; void ConfigManager::activeConfigChangedReceiverDied(void* cookie) { auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie); - sp<ConfigManager>& thiz = cookie_->mConfigManager; + sp<ConfigManager> thiz = cookie_->mConfigManager.promote(); + if (!thiz) { + return; + } + int uid = cookie_->mUid; shared_ptr<IPendingIntentRef>& pir = cookie_->mPir; diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 79a7e8d318e2..ebe961014336 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -44,19 +44,23 @@ namespace statsd { // Stores the puller as a wp to avoid holding a reference in case it is unregistered and // pullAtomCallbackDied is never called. struct PullAtomCallbackDeathCookie { - PullAtomCallbackDeathCookie(sp<StatsPullerManager> pullerManager, const PullerKey& pullerKey, - const wp<StatsPuller>& puller) - : mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) { + PullAtomCallbackDeathCookie(const wp<StatsPullerManager>& pullerManager, + const PullerKey& pullerKey, const wp<StatsPuller>& puller) : + mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) { } - sp<StatsPullerManager> mPullerManager; + wp<StatsPullerManager> mPullerManager; PullerKey mPullerKey; wp<StatsPuller> mPuller; }; void StatsPullerManager::pullAtomCallbackDied(void* cookie) { PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie); - sp<StatsPullerManager>& thiz = cookie_->mPullerManager; + sp<StatsPullerManager> thiz = cookie_->mPullerManager.promote(); + if (!thiz) { + return; + } + const PullerKey& pullerKey = cookie_->mPullerKey; wp<StatsPuller> puller = cookie_->mPuller; diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp index aee725698c30..90247cf9d68c 100644 --- a/cmds/statsd/src/external/puller_util.cpp +++ b/cmds/statsd/src/external/puller_util.cpp @@ -49,10 +49,14 @@ using namespace std; */ void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const sp<UidMap>& uidMap, int tagId, const vector<int>& additiveFieldsVec) { - if ((android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) == - android::util::AtomsInfo::kAtomsWithAttributionChain.end()) && - (android::util::AtomsInfo::kAtomsWithUidField.find(tagId) == - android::util::AtomsInfo::kAtomsWithUidField.end())) { + bool hasAttributionChain = (android::util::AtomsInfo::kAtomsWithAttributionChain.find(tagId) != + android::util::AtomsInfo::kAtomsWithAttributionChain.end()); + // To check if any LogEvent has a uid field, we can just check the first + // LogEvent because all atoms with this tagId should have the uid + // annotation. + bool hasUidField = (data[0]->getUidFieldIndex() != -1); + + if (!hasAttributionChain && !hasUidField) { VLOG("No uid or attribution chain to merge, atom %d", tagId); return; } @@ -75,19 +79,13 @@ void mapAndMergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data, const } } } else { - auto it = android::util::AtomsInfo::kAtomsWithUidField.find(tagId); - if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) { - int uidField = it->second; // uidField is the field number in proto, - // starting from 1 - if (uidField > 0 && (int)event->getValues().size() >= uidField && - (event->getValues())[uidField - 1].mValue.getType() == INT) { - Value& value = (*event->getMutableValues())[uidField - 1].mValue; - const int hostUid = uidMap->getHostUidOrSelf(value.int_value); - value.setInt(hostUid); - } else { - ALOGE("Malformed log, uid not found. %s", event->ToString().c_str()); - return; - } + int uidFieldIndex = event->getUidFieldIndex(); + if (uidFieldIndex != -1) { + Value& value = (*event->getMutableValues())[uidFieldIndex].mValue; + const int hostUid = uidMap->getHostUidOrSelf(value.int_value); + value.setInt(hostUid); + } else { + ALOGE("Malformed log, uid not found. %s", event->ToString().c_str()); } } } diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 96bf04f4f6d6..8b6a86464155 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -240,14 +240,20 @@ void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last, last[1] = last[2] = false; } +// Assumes that mValues is not empty +bool LogEvent::checkPreviousValueType(Type expected) { + return mValues[mValues.size() - 1].mValue.getType() == expected; +} + void LogEvent::parseIsUidAnnotation(uint8_t annotationType) { - if (mValues.empty() || annotationType != BOOL_TYPE) { + if (mValues.empty() || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) { mValid = false; return; } bool isUid = readNextValue<uint8_t>(); if (isUid) mUidFieldIndex = mValues.size() - 1; + mValues[mValues.size() - 1].mAnnotations.setUidField(isUid); } void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) { diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 9e21c777e6ff..4eeb7d64a463 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -213,6 +213,7 @@ private: void parseExclusiveStateAnnotation(uint8_t annotationType); void parseTriggerStateResetAnnotation(uint8_t annotationType); void parseStateNestedAnnotation(uint8_t annotationType); + bool checkPreviousValueType(Type expected); /** * The below three variables are only valid during the execution of diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index 1f8bbd7f528c..2b4c6a3cbf1e 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -81,18 +81,17 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera return matched; } -bool tryMatchString(const UidMap& uidMap, const Field& field, const Value& value, - const string& str_match) { - if (isAttributionUidField(field, value) || isUidField(field, value)) { - int uid = value.int_value; +bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) { + if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) { + int uid = fieldValue.mValue.int_value; auto aidIt = UidMap::sAidToUidMapping.find(str_match); if (aidIt != UidMap::sAidToUidMapping.end()) { return ((int)aidIt->second) == uid; } std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/); return packageNames.find(str_match) != packageNames.end(); - } else if (value.getType() == STRING) { - return value.str_value == str_match; + } else if (fieldValue.mValue.getType() == STRING) { + return fieldValue.mValue.str_value == str_match; } return false; } @@ -228,8 +227,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, } case FieldValueMatcher::ValueMatcherCase::kEqString: { for (int i = start; i < end; i++) { - if (tryMatchString(uidMap, values[i].mField, values[i].mValue, - matcher.eq_string())) { + if (tryMatchString(uidMap, values[i], matcher.eq_string())) { return true; } } @@ -240,7 +238,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, for (int i = start; i < end; i++) { bool notEqAll = true; for (const auto& str : str_list.str_value()) { - if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) { + if (tryMatchString(uidMap, values[i], str)) { notEqAll = false; break; } @@ -255,7 +253,7 @@ bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher, const auto& str_list = matcher.eq_any_string(); for (int i = start; i < end; i++) { for (const auto& str : str_list.str_value()) { - if (tryMatchString(uidMap, values[i].mField, values[i].mValue, str)) { + if (tryMatchString(uidMap, values[i], str)) { return true; } } diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp index 0bf24f1d3606..f5ba8fd0d60d 100644 --- a/cmds/statsd/tests/FieldValue_test.cpp +++ b/cmds/statsd/tests/FieldValue_test.cpp @@ -41,22 +41,10 @@ void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timest AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeString(statsEvent, name.c_str()); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, @@ -66,22 +54,10 @@ void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timest AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeInt32(statsEvent, value); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } } // anonymous namespace diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 6f4c8e48eff2..26423d464027 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -18,6 +18,7 @@ #include <log/logprint.h> #include <stdio.h> +#include "annotations.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "matchers/matcher_util.h" #include "stats_event.h" @@ -48,15 +49,9 @@ void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t tim AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeInt32(statsEvent, value); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, @@ -64,15 +59,9 @@ void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t t AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeFloat(statsEvent, floatValue); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, @@ -80,32 +69,20 @@ void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - AStatsEvent_writeString(statsEvent, name.c_str()); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } -void makeIntStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, - const int32_t value, const string& name) { +void makeIntWithBoolAnnotationLogEvent(LogEvent* logEvent, const int32_t atomId, + const int32_t field, const uint8_t annotationId, + const bool annotationValue) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - - AStatsEvent_writeInt32(statsEvent, value); - AStatsEvent_writeString(statsEvent, name.c_str()); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); + AStatsEvent_writeInt32(statsEvent, field); + AStatsEvent_addBoolAnnotation(statsEvent, annotationId, annotationValue); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, @@ -115,22 +92,10 @@ void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestamp); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeString(statsEvent, name.c_str()); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp, @@ -141,13 +106,8 @@ void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t ti AStatsEvent_writeBool(statsEvent, bool1); AStatsEvent_writeBool(statsEvent, bool2); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } } // anonymous namespace @@ -416,21 +376,20 @@ TEST(AtomMatcherTest, TestUidFieldMatcher) { simpleMatcher->add_field_value_matcher()->set_field(1); simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0"); - // Set up the event + // Make event without is_uid annotation. LogEvent event1(/*uid=*/0, /*pid=*/0); makeIntLogEvent(&event1, TAG_ID, 0, 1111); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); + // Make event with is_uid annotation. LogEvent event2(/*uid=*/0, /*pid=*/0); - makeIntStringLogEvent(&event2, TAG_ID_2, 0, 1111, "some value"); - - // Tag not in kAtomsWithUidField - EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); + makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID_2, 1111, ANNOTATION_ID_IS_UID, true); - // Tag found in kAtomsWithUidField and has matching uid + // Event has is_uid annotation, so mapping from uid to package name occurs. simpleMatcher->set_atom_id(TAG_ID_2); EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); - // Tag found in kAtomsWithUidField but has non-matching uid + // Event has is_uid annotation, but uid maps to different package name. simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2"); EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2)); } diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 7febb35355a3..ba5b032b80d0 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -67,22 +67,12 @@ void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp, AStatsEvent_overwriteTimestamp(statsEvent, timestamp); vector<std::string> tags(uids.size()); // vector of empty strings - vector<const char*> cTags(uids.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = tags[i].c_str(); - } - AStatsEvent_writeAttributionChain(statsEvent, reinterpret_cast<const uint32_t*>(uids.data()), - cTags.data(), uids.size()); + writeAttribution(statsEvent, uids, tags); AStatsEvent_writeString(statsEvent, wl.c_str()); AStatsEvent_writeInt32(statsEvent, acquire); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } } // anonymous namespace diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp index 81e1c05c1cf4..60403f2a3e0f 100644 --- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp +++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp @@ -84,14 +84,9 @@ std::unique_ptr<LogEvent> CreateAppStartOccurredEvent( AStatsEvent_writeString(statsEvent, calling_pkg_name.c_str()); AStatsEvent_writeInt32(statsEvent, is_instant_app); AStatsEvent_writeInt32(statsEvent, activity_start_msec); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp index 6aff9ef80a71..4b9bac127dc8 100644 --- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp +++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp @@ -190,13 +190,13 @@ TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) { int32_t uid = 123; values.push_back(value); - StatsPullerManager pullerManager; - pullerManager.RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs, - vector<int32_t>(), cb); + sp<StatsPullerManager> pullerManager = new StatsPullerManager(); + pullerManager->RegisterPullAtomCallback(uid, pullTagId, pullCoolDownNs, pullTimeoutNs, + vector<int32_t>(), cb); vector<shared_ptr<LogEvent>> dataHolder; int64_t startTimeNs = getElapsedRealtimeNs(); // Returns false, since StatsPuller code will evaluate the timeout. - EXPECT_FALSE(pullerManager.Pull(pullTagId, {uid}, &dataHolder)); + EXPECT_FALSE(pullerManager->Pull(pullTagId, {uid}, &dataHolder)); int64_t endTimeNs = getElapsedRealtimeNs(); int64_t actualPullDurationNs = endTimeNs - startTimeNs; diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp index e8200d5c7f52..504335845cab 100644 --- a/cmds/statsd/tests/external/StatsPuller_test.cpp +++ b/cmds/statsd/tests/external/StatsPuller_test.cpp @@ -64,16 +64,10 @@ std::unique_ptr<LogEvent> createSimpleEvent(int64_t eventTimeNs, int64_t value) AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, pullTagId); AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - AStatsEvent_writeInt64(statsEvent, value); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp index 15425d8810dc..c2cfb371d329 100644 --- a/cmds/statsd/tests/external/puller_util_test.cpp +++ b/cmds/statsd/tests/external/puller_util_test.cpp @@ -21,8 +21,10 @@ #include <vector> #include "../metrics/metrics_test_helper.h" +#include "annotations.h" #include "stats_event.h" #include "statslog_statsdtest.h" +#include "tests/statsd_test_util.h" #ifdef __ANDROID__ @@ -52,15 +54,15 @@ int hostNonAdditiveData = 22; void extractIntoVector(vector<shared_ptr<LogEvent>> events, vector<vector<int>>& ret) { - ret.clear(); - status_t err; - for (const auto& event : events) { - vector<int> vec; - vec.push_back(event->GetInt(1, &err)); - vec.push_back(event->GetInt(2, &err)); - vec.push_back(event->GetInt(3, &err)); - ret.push_back(vec); - } + ret.clear(); + status_t err; + for (const auto& event : events) { + vector<int> vec; + vec.push_back(event->GetInt(1, &err)); + vec.push_back(event->GetInt(2, &err)); + vec.push_back(event->GetInt(3, &err)); + ret.push_back(vec); + } } std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int data1, int data2) { @@ -69,16 +71,12 @@ std::shared_ptr<LogEvent> makeUidLogEvent(uint64_t timestampNs, int uid, int dat AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); AStatsEvent_writeInt32(statsEvent, uid); + AStatsEvent_addBoolAnnotation(statsEvent, ANNOTATION_ID_IS_UID, true); AStatsEvent_writeInt32(statsEvent, data1); AStatsEvent_writeInt32(statsEvent, data2); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -86,16 +84,10 @@ std::shared_ptr<LogEvent> makeNonUidAtomLogEvent(uint64_t timestampNs, int data1 AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, nonUidAtomTagId); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, data1); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::shared_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } diff --git a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp index 6dc041f9fb6e..a15f95bef358 100644 --- a/cmds/statsd/tests/log_event/LogEventQueue_test.cpp +++ b/cmds/statsd/tests/log_event/LogEventQueue_test.cpp @@ -21,6 +21,7 @@ #include <thread> #include "stats_event.h" +#include "tests/statsd_test_util.h" namespace android { namespace os { @@ -37,14 +38,9 @@ std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, 10); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index d55996cb1b7a..65f8de69711d 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -46,26 +46,17 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeString(statsEvent, uid.c_str()); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } } // namespace diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 6143dc0dc5d1..30f815962160 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -26,6 +26,7 @@ #include "src/condition/ConditionWizard.h" #include "src/stats_log_util.h" #include "stats_event.h" +#include "tests/statsd_test_util.h" using namespace android::os::statsd; using namespace testing; @@ -48,12 +49,8 @@ void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } } // namespace diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index e58bbb7893d7..97647a7e0867 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -43,14 +43,9 @@ void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, strin AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeString(statsEvent, str.c_str()); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } } // anonymous namespace diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 2fe05a4430c3..42d0d5d8c530 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -64,14 +64,9 @@ shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t v AStatsEvent_writeInt32(statsEvent, value1); AStatsEvent_writeString(statsEvent, str1.c_str()); AStatsEvent_writeInt32(statsEvent, value2); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); - + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } } // anonymous namespace diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index b623a0978f18..009e49a5523f 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -611,7 +611,7 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { vector<shared_ptr<LogEvent>> allData; allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 110)); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}); @@ -656,7 +656,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); @@ -665,14 +665,14 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 10); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(bucketStartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs); // Next value should create a new bucket. LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10); + CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 10); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs); @@ -812,10 +812,10 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); // has one slice @@ -856,7 +856,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { valueProducer.mCondition = ConditionState::kFalse; LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); // has 1 slice EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); @@ -864,7 +864,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { valueProducer.onConditionChangedLocked(true, bucketStartTimeNs + 15); LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event2); // has one slice @@ -875,7 +875,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { EXPECT_EQ(20, curInterval.value.long_value); LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 1, 30); + CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 30); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); // has one slice @@ -886,7 +886,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35); LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 1, 40); + CreateRepeatedValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 40); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); // has one slice @@ -1195,10 +1195,10 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); // has one slice @@ -1238,10 +1238,10 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 20); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); // has one slice @@ -1283,10 +1283,10 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 15); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); @@ -1331,10 +1331,10 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 1, 15); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); @@ -1374,10 +1374,10 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { valueProducer.prepareFirstBucket(); LogEvent event1(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10); + CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10); LogEvent event2(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15); + CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); @@ -1398,7 +1398,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { // no change in data. LogEvent event3(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15); + CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; @@ -1408,7 +1408,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { EXPECT_EQ(true, curInterval.hasValue); LogEvent event4(/*uid=*/0, /*pid=*/0); - CreateTwoValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15); + CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; @@ -2166,7 +2166,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) // Bucket start. vector<shared_ptr<LogEvent>> allData; allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 1, 1, 110)); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110)); valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); @@ -2174,7 +2174,7 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) // Bucket end. allData.clear(); - allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 140)); + allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140)); valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp index ac3ad690f81e..7b952d7a392e 100644 --- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp +++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp @@ -171,13 +171,9 @@ shared_ptr<LogEvent> makeCpuActiveTimeAtom(int32_t uid, int64_t timeMillis) { AStatsEvent_overwriteTimestamp(statsEvent, 1111L); AStatsEvent_writeInt32(statsEvent, uid); AStatsEvent_writeInt64(statsEvent, timeMillis); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp index a5b8e1c50c33..78c80bc8307c 100644 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -62,7 +62,7 @@ int getStateInt(StateManager& mgr, int atomId, const HashableDimensionKey& query // START: build event functions. // Incorrect event - missing fields -std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, +std::unique_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED); @@ -72,14 +72,9 @@ std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& AStatsEvent_writeString(statsEvent, packageName.c_str()); // Missing field 3 - using_alert_window. AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -93,14 +88,9 @@ std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::stri AStatsEvent_writeString(statsEvent, packageName.c_str()); AStatsEvent_writeInt32(statsEvent, true); // using_alert_window AStatsEvent_writeString(statsEvent, "string"); // exclusive state: string instead of int - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } // END: build event functions. diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 7d765d3fbbf5..ed3cf5b96b42 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -507,23 +507,26 @@ void getPartialWakelockKey(int uid, HashableDimensionKey* key) { } // END: get primary key functions -shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); +void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids, + const vector<string>& attributionTags) { + vector<const char*> cTags(attributionTags.size()); + for (int i = 0; i < cTags.size(); i++) { + cTags[i] = attributionTags[i].c_str(); + } - AStatsEvent_writeInt32(statsEvent, value1); - AStatsEvent_writeInt32(statsEvent, value2); + AStatsEvent_writeAttributionChain(statsEvent, + reinterpret_cast<const uint32_t*>(attributionUids.data()), + cTags.data(), attributionUids.size()); +} + +void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) { AStatsEvent_build(statsEvent); size_t size; uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); - return logEvent; + AStatsEvent_release(statsEvent); } void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1, @@ -534,31 +537,14 @@ void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, AStatsEvent_writeInt32(statsEvent, value1); AStatsEvent_writeInt32(statsEvent, value2); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } -shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, - int32_t value2, int32_t value3) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, value1); - AStatsEvent_writeInt32(statsEvent, value2); - AStatsEvent_writeInt32(statsEvent, value3); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); +shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, + int32_t value2) { shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); - + CreateTwoValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2); return logEvent; } @@ -571,29 +557,14 @@ void CreateThreeValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeN AStatsEvent_writeInt32(statsEvent, value1); AStatsEvent_writeInt32(statsEvent, value2); AStatsEvent_writeInt32(statsEvent, value3); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } -shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - - AStatsEvent_writeInt32(statsEvent, value); - AStatsEvent_writeInt32(statsEvent, value); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); +shared_ptr<LogEvent> CreateThreeValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, + int32_t value2, int32_t value3) { shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); - + CreateThreeValueLogEvent(logEvent.get(), atomId, eventTimeNs, value1, value2, value3); return logEvent; } @@ -605,26 +576,13 @@ void CreateRepeatedValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTi AStatsEvent_writeInt32(statsEvent, value); AStatsEvent_writeInt32(statsEvent, value); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); } -shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) { - AStatsEvent* statsEvent = AStatsEvent_obtain(); - AStatsEvent_setAtomId(statsEvent, atomId); - AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); +shared_ptr<LogEvent> CreateRepeatedValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value) { shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); - + CreateRepeatedValueLogEvent(logEvent.get(), atomId, eventTimeNs, value); return logEvent; } @@ -632,12 +590,14 @@ void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs) AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, atomId); AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs); - AStatsEvent_build(statsEvent); - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent); +} + +shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs) { + shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0); + CreateNoValuesLogEvent(logEvent.get(), atomId, eventTimeNs); + return logEvent; } std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( @@ -645,16 +605,10 @@ std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -662,16 +616,10 @@ std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::ON); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -679,16 +627,10 @@ std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) { AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, util::BATTERY_SAVER_MODE_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, BatterySaverModeStateChanged::OFF); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -696,16 +638,10 @@ std::unique_ptr<LogEvent> CreateBatteryStateChangedEvent(const uint64_t timestam AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, util::PLUGGED_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -713,16 +649,10 @@ std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampN AStatsEvent* statsEvent = AStatsEvent_obtain(); AStatsEvent_setAtomId(statsEvent, util::SCREEN_BRIGHTNESS_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - AStatsEvent_writeInt32(statsEvent, level); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -733,24 +663,12 @@ std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent( AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeString(statsEvent, jobName.c_str()); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -780,25 +698,13 @@ std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs, AStatsEvent_setAtomId(statsEvent, util::WAKELOCK_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeInt32(statsEvent, android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK); AStatsEvent_writeString(statsEvent, wakelockName.c_str()); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -828,14 +734,9 @@ std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( AStatsEvent_writeString(statsEvent, "pkg_name"); AStatsEvent_writeString(statsEvent, "class_name"); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -858,24 +759,12 @@ std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs, AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeString(statsEvent, name.c_str()); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -904,14 +793,9 @@ std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent( AStatsEvent_writeInt32(statsEvent, uid); AStatsEvent_writeString(statsEvent, ""); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -928,14 +812,9 @@ std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, cons AStatsEvent_writeInt32(statsEvent, uid); AStatsEvent_writeString(statsEvent, "eventType"); AStatsEvent_writeString(statsEvent, "processName"); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -948,14 +827,9 @@ std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(uint64_t timestampNs, in AStatsEvent_writeInt32(statsEvent, hostUid); AStatsEvent_writeInt32(statsEvent, isolatedUid); AStatsEvent_writeInt32(statsEvent, is_create); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -967,14 +841,9 @@ std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent( AStatsEvent_writeInt32(statsEvent, uid); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -988,26 +857,14 @@ std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs, AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED); AStatsEvent_overwriteTimestamp(statsEvent, timestampNs); - vector<const char*> cTags(attributionTags.size()); - for (int i = 0; i < cTags.size(); i++) { - cTags[i] = attributionTags[i].c_str(); - } - - AStatsEvent_writeAttributionChain(statsEvent, - reinterpret_cast<const uint32_t*>(attributionUids.data()), - cTags.data(), attributionUids.size()); + writeAttribution(statsEvent, attributionUids, attributionTags); AStatsEvent_writeInt32(statsEvent, state); AStatsEvent_writeInt32(statsEvent, filtered); // filtered AStatsEvent_writeInt32(statsEvent, firstMatch); // first match AStatsEvent_writeInt32(statsEvent, opportunistic); // opportunistic - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } @@ -1023,14 +880,9 @@ std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, co AStatsEvent_writeString(statsEvent, packageName.c_str()); AStatsEvent_writeInt32(statsEvent, usingAlertWindow); AStatsEvent_writeInt32(statsEvent, state); - AStatsEvent_build(statsEvent); - - size_t size; - uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size); std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0); - logEvent->parseBuffer(buf, size); - AStatsEvent_release(statsEvent); + parseStatsEventToLogEvent(statsEvent, logEvent.get()); return logEvent; } diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index f24705a0c89f..d6ea77eb2c7d 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -25,6 +25,7 @@ #include "src/hash.h" #include "src/logd/LogEvent.h" #include "src/stats_log_util.h" +#include "stats_event.h" #include "statslog_statsdtest.h" namespace android { @@ -189,6 +190,12 @@ void getPartialWakelockKey(int uid, const std::string& tag, HashableDimensionKey void getPartialWakelockKey(int uid, HashableDimensionKey* key); // END: get primary key functions +void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids, + const vector<string>& attributionTags); + +// Builds statsEvent to get buffer that is parsed into logEvent then releases statsEvent. +void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent); + shared_ptr<LogEvent> CreateTwoValueLogEvent(int atomId, int64_t eventTimeNs, int32_t value1, int32_t value2); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 46b06fb64b80..3a708a6f699b 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1395,6 +1395,7 @@ public class AppOpsManager { public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages"; /** @hide Access all external storage */ @SystemApi + @TestApi public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage"; diff --git a/core/java/android/app/IWindowToken.aidl b/core/java/android/app/IWindowToken.aidl index 8ea881fba09c..3627b0f13a7f 100644 --- a/core/java/android/app/IWindowToken.aidl +++ b/core/java/android/app/IWindowToken.aidl @@ -30,4 +30,6 @@ import android.view.IWindow; */ oneway interface IWindowToken { void onConfigurationChanged(in Configuration newConfig, int newDisplayId); + + void onWindowTokenRemoved(); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 91a857225324..e599a5ce81ef 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -1658,6 +1658,9 @@ public final class SystemServiceRegistry { public final T getService(ContextImpl ctx) { final Object[] cache = ctx.mServiceCache; final int[] gates = ctx.mServiceInitializationStateArray; + boolean interrupted = false; + + T ret = null; for (;;) { boolean doInitialize = false; @@ -1665,7 +1668,8 @@ public final class SystemServiceRegistry { // Return it if we already have a cached instance. T service = (T) cache[mCacheIndex]; if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) { - return service; + ret = service; + break; // exit the for (;;) } // If we get here, there's no cached instance. @@ -1708,24 +1712,33 @@ public final class SystemServiceRegistry { cache.notifyAll(); } } - return service; + ret = service; + break; // exit the for (;;) } // The other threads will wait for the first thread to call notifyAll(), // and go back to the top and retry. synchronized (cache) { + // Repeat until the state becomes STATE_READY or STATE_NOT_FOUND. + // We can't respond to interrupts here; just like we can't in the "doInitialize" + // path, so we remember the interrupt state here and re-interrupt later. while (gates[mCacheIndex] < ContextImpl.STATE_READY) { try { + // Clear the interrupt state. + interrupted |= Thread.interrupted(); cache.wait(); } catch (InterruptedException e) { // This shouldn't normally happen, but if someone interrupts the // thread, it will. - Slog.wtf(TAG, "getService() interrupted"); - Thread.currentThread().interrupt(); - return null; + Slog.w(TAG, "getService() interrupted"); + interrupted = true; } } } } + if (interrupted) { + Thread.currentThread().interrupt(); + } + return ret; } public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException; diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java index 878993ebcd19..3a06c9d79fee 100644 --- a/core/java/android/app/WindowContext.java +++ b/core/java/android/app/WindowContext.java @@ -28,6 +28,8 @@ import android.view.IWindowManager; import android.view.WindowManagerGlobal; import android.view.WindowManagerImpl; +import com.android.internal.annotations.VisibleForTesting; + import java.lang.ref.Reference; /** @@ -75,8 +77,6 @@ public class WindowContext extends ContextWrapper { // config back to the client. result = mWms.addWindowTokenWithOptions( mToken, type, getDisplayId(), options, getPackageName()); - - // TODO(window-context): remove token with a DeathObserver } catch (RemoteException e) { mOwnsToken = false; throw e.rethrowFromSystemServer(); @@ -100,6 +100,13 @@ public class WindowContext extends ContextWrapper { @Override protected void finalize() throws Throwable { + release(); + super.finalize(); + } + + /** Used for test to invoke because we can't invoke finalize directly. */ + @VisibleForTesting + public void release() { if (mOwnsToken) { try { mWms.removeWindowToken(mToken, getDisplayId()); @@ -108,6 +115,12 @@ public class WindowContext extends ContextWrapper { throw e.rethrowFromSystemServer(); } } - super.finalize(); + destroy(); + } + + void destroy() { + final ContextImpl impl = (ContextImpl) getBaseContext(); + impl.scheduleFinalCleanup(getClass().getName(), "WindowContext"); + Reference.reachabilityFence(this); } } diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java index ed0179bb9839..301960ec53f9 100644 --- a/core/java/android/app/WindowTokenClient.java +++ b/core/java/android/app/WindowTokenClient.java @@ -20,6 +20,9 @@ import android.content.Context; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; +import android.view.WindowManagerGlobal; + +import java.lang.ref.WeakReference; /** * Client implementation of {@link IWindowToken}. It can receive configuration change callbacks from @@ -31,9 +34,9 @@ import android.os.IBinder; public class WindowTokenClient extends IWindowToken.Stub { /** * Attached {@link Context} for this window token to update configuration and resources. - * Initialized by {@link #attachContext(Context)}. + * Initialized by {@link #attachContext(WindowContext)}. */ - private Context mContext = null; + private WeakReference<WindowContext> mContextRef = null; private final ResourcesManager mResourcesManager = ResourcesManager.getInstance(); @@ -47,30 +50,46 @@ public class WindowTokenClient extends IWindowToken.Stub { * @param context context to be attached * @throws IllegalStateException if attached context has already existed. */ - void attachContext(@NonNull Context context) { - if (mContext != null) { + void attachContext(@NonNull WindowContext context) { + if (mContextRef != null) { throw new IllegalStateException("Context is already attached."); } - mContext = context; - ContextImpl impl = ContextImpl.getImpl(mContext); + mContextRef = new WeakReference<>(context); + final ContextImpl impl = ContextImpl.getImpl(context); impl.setResources(impl.createWindowContextResources()); } @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { - final int currentDisplayId = mContext.getDisplayId(); + final Context context = mContextRef.get(); + if (context == null) { + return; + } + final int currentDisplayId = context.getDisplayId(); final boolean displayChanged = newDisplayId != currentDisplayId; - final Configuration config = new Configuration(mContext.getResources() + final Configuration config = new Configuration(context.getResources() .getConfiguration()); final boolean configChanged = config.isOtherSeqNewer(newConfig) && config.updateFrom(newConfig) != 0; if (displayChanged || configChanged) { // TODO(ag/9789103): update resource manager logic to track non-activity tokens - mResourcesManager.updateResourcesForActivity(asBinder(), config, newDisplayId, + mResourcesManager.updateResourcesForActivity(this, config, newDisplayId, displayChanged); } if (displayChanged) { - mContext.updateDisplay(newDisplayId); + context.updateDisplay(newDisplayId); + } + } + + @Override + public void onWindowTokenRemoved() { + final WindowContext context = mContextRef.get(); + if (context != null) { + context.destroy(); + mContextRef.clear(); } + // If a secondary display is detached, release all views attached to this token. + WindowManagerGlobal.getInstance().closeAll(this, mContextRef.getClass().getName(), + "WindowContext"); } } diff --git a/core/java/android/app/admin/DevicePolicyKeyguardService.java b/core/java/android/app/admin/DevicePolicyKeyguardService.java index db833ec478bd..473725f40cf1 100644 --- a/core/java/android/app/admin/DevicePolicyKeyguardService.java +++ b/core/java/android/app/admin/DevicePolicyKeyguardService.java @@ -16,12 +16,15 @@ package android.app.admin; +import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; +import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.view.SurfaceControlViewHost; @@ -41,27 +44,34 @@ import android.view.SurfaceControlViewHost; @SystemApi public class DevicePolicyKeyguardService extends Service { private static final String TAG = "DevicePolicyKeyguardService"; + private final Handler mHandler = new Handler(Looper.getMainLooper()); private IKeyguardCallback mCallback; private final IKeyguardClient mClient = new IKeyguardClient.Stub() { + @MainThread @Override public void onCreateKeyguardSurface(@Nullable IBinder hostInputToken, - IKeyguardCallback callback) { + @NonNull IKeyguardCallback callback) { mCallback = callback; - SurfaceControlViewHost.SurfacePackage surfacePackage = - DevicePolicyKeyguardService.this.onCreateKeyguardSurface(hostInputToken); + mHandler.post(() -> { + SurfaceControlViewHost.SurfacePackage surfacePackage = + DevicePolicyKeyguardService.this.onCreateKeyguardSurface(hostInputToken); - if (mCallback != null) { try { mCallback.onRemoteContentReady(surfacePackage); } catch (RemoteException e) { Log.e(TAG, "Failed to return created SurfacePackage", e); } - } + }); } }; @Override + public void onDestroy() { + mHandler.removeCallbacksAndMessages(null); + } + + @Override @Nullable public final IBinder onBind(@Nullable Intent intent) { return mClient.asBinder(); @@ -97,6 +107,10 @@ public class DevicePolicyKeyguardService extends Service { */ @Nullable public void dismiss() { + if (mCallback == null) { + Log.w(TAG, "KeyguardCallback was unexpectedly null"); + return; + } try { mCallback.onDismiss(); } catch (RemoteException e) { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index fc48e7f18f5f..f216db6fc717 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -979,14 +979,17 @@ public final class BluetoothAdapter { 8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) { @Override protected Integer recompute(Void query) { - // This function must be called while holding the - // mServiceLock, and with mService not null. The public - // getState() method makes this guarantee. try { - return mService.getState(); + mServiceLock.readLock().lock(); + if (mService != null) { + return mService.getState(); + } } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); } + return BluetoothAdapter.STATE_OFF; } }; @@ -1013,24 +1016,7 @@ public final class BluetoothAdapter { @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState public int getState() { - int state = BluetoothAdapter.STATE_OFF; - - try { - mServiceLock.readLock().lock(); - // The test for mService must either be outside the cache, or - // the cache must be invalidated when mService changes. - if (mService != null) { - state = mBluetoothGetStateCache.query(null); - } - } catch (RuntimeException e) { - if (e.getCause() instanceof RemoteException) { - Log.e(TAG, "", e.getCause()); - } else { - throw e; - } - } finally { - mServiceLock.readLock().unlock(); - } + int state = mBluetoothGetStateCache.query(null); // Consider all internal states as OFF if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index e446f4fa5eb4..0a4627da223a 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -687,6 +687,19 @@ public abstract class ContentResolver implements ContentInterface { public static final int NOTIFY_DELETE = 1 << 4; /** + * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: typically set + * by a {@link ContentProvider} to indicate that this notification should + * not be subject to any delays when dispatching to apps running in the + * background. + * <p> + * Using this flag may negatively impact system health and performance, and + * should be used sparingly. + * + * @hide + */ + public static final int NOTIFY_NO_DELAY = 1 << 15; + + /** * No exception, throttled by app standby normally. * @hide */ diff --git a/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java b/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java index 6f01796c93c2..5d4b48dac46b 100644 --- a/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java +++ b/core/java/android/database/sqlite/SQLiteCantOpenDatabaseException.java @@ -22,4 +22,9 @@ public class SQLiteCantOpenDatabaseException extends SQLiteException { public SQLiteCantOpenDatabaseException(String error) { super(error); } + + /** @hide */ + public SQLiteCantOpenDatabaseException(String error, Throwable cause) { + super(error, cause); + } } diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index bcb3934a5b08..f7c96a3a02c1 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -36,6 +36,9 @@ import dalvik.system.CloseGuard; import java.io.File; import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -215,12 +218,31 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen } private void open() { + final String file = mConfiguration.path; final int cookie = mRecentOperations.beginOperation("open", null, null); try { - mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags, + mConnectionPtr = nativeOpen(file, mConfiguration.openFlags, mConfiguration.label, NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME, mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount); + } catch (SQLiteCantOpenDatabaseException e) { + String message = String.format("Cannot open database '%s'", file); + + final Path path = FileSystems.getDefault().getPath(file); + final Path dir = path.getParent(); + + if (!Files.isDirectory(dir)) { + message += ": Directory " + dir + " doesn't exist"; + } else if (!Files.exists(path)) { + message += ": File " + path + " doesn't exist"; + } else if (!Files.isReadable(path)) { + message += ": File " + path + " is not readable"; + } else if (Files.isDirectory(path)) { + message += ": Path " + path + " is a directory"; + } else { + message += ": Unknown reason"; + } + throw new SQLiteCantOpenDatabaseException(message, e); } finally { mRecentOperations.endOperation(cookie); } diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 91dae66d08ae..aa75f6042db8 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -17,6 +17,7 @@ package android.hardware.camera2; import android.annotation.NonNull; +import android.compat.annotation.UnsupportedAppUsage; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.impl.PublicKey; import android.hardware.camera2.impl.SyntheticKey; @@ -100,6 +101,8 @@ public abstract class CameraMetadata<TKey> { * * @hide */ + @UnsupportedAppUsage(publicAlternatives = "This method is exposed for native " + + "{@code ACameraMetadata_fromCameraMetadata} in {@code libcamera2ndk}.") public long getNativeMetadataPtr() { if (mNativeInstance == null) { return 0; diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 84ac90bae258..5648adfb78bc 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -124,13 +124,20 @@ interface IWindowManager * @param type Window type to be used with this token. * @param options A bundle used to pass window-related options. * @param displayId The ID of the display where this token should be added. - * @param packageName The name of package to request to add window token. + * @param packageName The name of package to request to add window token. Could be {@code null} + * if callers holds the MANAGE_APP_TOKENS permission. * @return {@link WindowManagerGlobal#ADD_OKAY} if the addition was successful, an error code * otherwise. */ int addWindowTokenWithOptions(IBinder token, int type, int displayId, in Bundle options, String packageName); void addWindowToken(IBinder token, int type, int displayId); + /** + * Remove window token on a specific display. + * + * @param token Token to be removed + * @displayId The ID of the display where this token should be removed. + */ void removeWindowToken(IBinder token, int displayId); void prepareAppTransition(int transit, boolean alwaysKeepCurrent); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index ab65e3a5c849..1086774fc8ff 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -2888,7 +2888,8 @@ public final class SurfaceControl implements Parcelable { /** * Acquire a frame rate flexibility token, which allows surface flinger to freely switch display * frame rates. This is used by CTS tests to put the device in a consistent state. See - * ISurfaceComposer::acquireFrameRateFlexibilityToken(). + * ISurfaceComposer::acquireFrameRateFlexibilityToken(). The caller must have the + * ACCESS_SURFACE_FLINGER permission, or else the call will fail, returning 0. * @hide */ @TestApi diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java index 842ba2975b3b..6ad5cb913553 100644 --- a/core/java/android/view/textclassifier/ConversationActions.java +++ b/core/java/android/view/textclassifier/ConversationActions.java @@ -27,8 +27,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.SpannedString; -import com.android.internal.util.Preconditions; - import java.lang.annotation.Retention; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -491,7 +489,11 @@ public final class ConversationActions implements Parcelable { */ @NonNull public Builder setMaxSuggestions(@IntRange(from = -1) int maxSuggestions) { - mMaxSuggestions = Preconditions.checkArgumentNonnegative(maxSuggestions); + if (maxSuggestions < -1) { + throw new IllegalArgumentException("maxSuggestions has to be greater than or " + + "equal to -1."); + } + mMaxSuggestions = maxSuggestions; return this; } diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index eee222b9bf4c..6ae70b779960 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -33,8 +33,8 @@ public class DisplayAreaOrganizer extends WindowOrganizer { public static final int FEATURE_SYSTEM_FIRST = 0; // The Root display area on a display public static final int FEATURE_ROOT = FEATURE_SYSTEM_FIRST; - // Display area hosting the task container. - public static final int FEATURE_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1; + // Display area hosting the default task container. + public static final int FEATURE_DEFAULT_TASK_CONTAINER = FEATURE_SYSTEM_FIRST + 1; // Display area hosting non-activity window tokens. public static final int FEATURE_WINDOW_TOKENS = FEATURE_SYSTEM_FIRST + 2; diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 71cf5cad316e..b6c58e16b51c 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -68,7 +68,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.LayerDrawable; -import android.os.Build.VERSION_CODES; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; @@ -120,7 +119,6 @@ import com.android.internal.widget.DecorCaptionView; import com.android.internal.widget.FloatingToolbar; import java.util.List; -import java.util.function.Function; /** @hide */ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { @@ -283,11 +281,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private Insets mLastBackgroundInsets = Insets.NONE; private boolean mDrawLegacyNavigationBarBackground; - /** - * Whether the app targets an SDK that uses the new insets APIs. - */ - private boolean mUseNewInsetsApi; - private PendingInsetsController mPendingInsetsController = new PendingInsetsController(); DecorView(Context context, int featureId, PhoneWindow window, @@ -319,7 +312,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind initResizingPaints(); mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK); - mUseNewInsetsApi = context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.R; } void setBackgroundFallback(@Nullable Drawable fallbackDrawable) { @@ -1189,23 +1181,23 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // these flags wouldn't make the window draw behind the navigation bar, unless // LAYOUT_HIDE_NAVIGATION was set. // - // Note: Once the app targets R+, we no longer do this logic because we can't rely on - // SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION to indicate whether the app wants to handle it by - // themselves. + // Note: Once the app uses the R+ Window.setDecorFitsSystemWindows(false) API we no longer + // consume insets because they might no longer set SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION. boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 || !(controller == null || controller.isRequestedVisible(ITYPE_NAVIGATION_BAR)); + boolean decorFitsSystemWindows = mWindow.mDecorFitsSystemWindows; boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds && (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0 + && decorFitsSystemWindows && !hideNavigation) || (mLastShouldAlwaysConsumeSystemBars && hideNavigation); boolean consumingNavBar = ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0 - && !hideNavigation - // TODO IME wrap_content windows need to have margin to work properly - && (!mUseNewInsetsApi || isImeWindow)) + && decorFitsSystemWindows + && !hideNavigation) || forceConsumingNavBar; // If we didn't request fullscreen layout, but we still got it because of the @@ -1216,6 +1208,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind || (attrs.flags & FLAG_FULLSCREEN) != 0 || !(controller == null || controller.isRequestedVisible(ITYPE_STATUS_BAR)); boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0 + && decorFitsSystemWindows && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0 && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0 && mForceWindowDrawsBarBackgrounds diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 138d0dd39537..25c114f4b7c1 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -343,8 +343,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** @see ViewRootImpl#mActivityConfigCallback */ private ActivityConfigCallback mActivityConfigCallback; - private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener = - sDefaultContentInsetsApplier; + boolean mDecorFitsSystemWindows = true; static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( @@ -2138,9 +2137,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */ void onViewRootImplSet(ViewRootImpl viewRoot) { viewRoot.setActivityConfigCallback(mActivityConfigCallback); - viewRoot.setOnContentApplyWindowInsetsListener( - mPendingOnContentApplyWindowInsetsListener); - mPendingOnContentApplyWindowInsetsListener = null; + applyDecorFitsSystemWindows(); } static private final String FOCUSED_ID_TAG = "android:focusedViewId"; @@ -3907,14 +3904,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) { + mDecorFitsSystemWindows = decorFitsSystemWindows; + applyDecorFitsSystemWindows(); + } + + private void applyDecorFitsSystemWindows() { ViewRootImpl impl = getViewRootImplOrNull(); - OnContentApplyWindowInsetsListener listener = decorFitsSystemWindows - ? sDefaultContentInsetsApplier - : null; if (impl != null) { - impl.setOnContentApplyWindowInsetsListener(listener); - } else { - mPendingOnContentApplyWindowInsetsListener = listener; + impl.setOnContentApplyWindowInsetsListener(mDecorFitsSystemWindows + ? sDefaultContentInsetsApplier + : null); } } } diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 684a29249294..896ee4fa42a5 100644 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto @@ -186,4 +186,15 @@ enum EventId { RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK= 159; RESOLVER_EMPTY_STATE_NO_APPS_RESOLVED= 160; RESOLVER_AUTOLAUNCH_CROSS_PROFILE_TARGET = 161; + CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_APP = 162; + CROSS_PROFILE_SETTINGS_PAGE_LAUNCHED_FROM_SETTINGS = 163; + CROSS_PROFILE_SETTINGS_PAGE_ADMIN_RESTRICTED = 164; + CROSS_PROFILE_SETTINGS_PAGE_MISSING_WORK_APP = 165; + CROSS_PROFILE_SETTINGS_PAGE_MISSING_PERSONAL_APP = 166; + CROSS_PROFILE_SETTINGS_PAGE_MISSING_INSTALL_BANNER_INTENT = 167; + CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_CLICKED = 168; + CROSS_PROFILE_SETTINGS_PAGE_INSTALL_BANNER_NO_INTENT_CLICKED = 169; + CROSS_PROFILE_SETTINGS_PAGE_USER_CONSENTED = 170; + CROSS_PROFILE_SETTINGS_PAGE_USER_DECLINED_CONSENT = 171; + CROSS_PROFILE_SETTINGS_PAGE_PERMISSION_REVOKED = 172; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 451363f6bd3d..c72239cd684e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4122,7 +4122,10 @@ set of pages referenced over time. <p>Declaring the permission implies intention to use the API and the user of the device can grant permission through the Settings application. - <p>Protection level: signature|privileged|appop --> + <p>Protection level: signature|privileged|appop + <p>A data loader has to be the one which provides data to install an app. + <p>A data loader has to have both permission:LOADER_USAGE_STATS AND + appop:LOADER_USAGE_STATS allowed to be able to access the read logs. --> <permission android:name="android.permission.LOADER_USAGE_STATS" android:protectionLevel="signature|privileged|appop" /> <uses-permission android:name="android.permission.LOADER_USAGE_STATS" /> diff --git a/core/tests/coretests/src/android/app/WindowContextTest.java b/core/tests/coretests/src/android/app/WindowContextTest.java new file mode 100644 index 000000000000..630e16ac80d4 --- /dev/null +++ b/core/tests/coretests/src/android/app/WindowContextTest.java @@ -0,0 +1,74 @@ +/* + * 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 android.app; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.os.IBinder; +import android.platform.test.annotations.Presubmit; +import android.view.Display; +import android.view.IWindowManager; +import android.view.WindowManagerGlobal; + +import androidx.test.filters.FlakyTest; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link WindowContext} + * + * <p>Build/Install/Run: + * atest FrameworksCoreTests:WindowContextTest + * + * <p>This test class is a part of Window Manager Service tests and specified in + * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}. + */ +@FlakyTest(bugId = 150812449, detail = "Remove after confirmed it's stable.") +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class WindowContextTest { + @Test + public void testWindowContextRelease_doRemoveWindowToken() throws Throwable { + final Context instContext = InstrumentationRegistry.getInstrumentation() + .getTargetContext(); + final Display display = instContext.getSystemService(DisplayManager.class) + .getDisplay(DEFAULT_DISPLAY); + final Context context = instContext.createDisplayContext(display); + final WindowContext windowContext = new WindowContext(context, TYPE_APPLICATION_OVERLAY, + null /* options */); + + final IBinder token = windowContext.getActivityToken(); + + final IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); + assertTrue("Token must be registered to WMS", wms.isWindowToken(token)); + + windowContext.release(); + + assertFalse("Token must be unregistered to WMS", wms.isWindowToken(token)); + } +} diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java index 74524bf6d76f..ddc977d380ae 100644 --- a/core/tests/coretests/src/android/view/WindowMetricsTest.java +++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java @@ -56,17 +56,17 @@ public class WindowMetricsTest { @Before public void setUp() { - final Context insetContext = InstrumentationRegistry.getInstrumentation() + final Context instContext = InstrumentationRegistry.getInstrumentation() .getTargetContext(); - final Display display = insetContext.getSystemService(DisplayManager.class) + final Display display = instContext.getSystemService(DisplayManager.class) .getDisplay(DEFAULT_DISPLAY); - mWindowContext = insetContext.createDisplayContext(display) + mWindowContext = instContext.createDisplayContext(display) .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */); mWm = mWindowContext.getSystemService(WindowManager.class); } @Test - public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() { + public void testAddViewAndRemoveView_GetMetrics_DoNotCrash() { final View view = new View(mWindowContext); final WindowManager.LayoutParams params = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 59bdf3dad43e..0389639e9edb 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -137,6 +137,7 @@ applications that come with the platform <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" /> <permission name="android.permission.PACKAGE_USAGE_STATS" /> + <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> </privapp-permissions> <privapp-permissions package="com.android.phone"> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 07cf41560cf3..19ad6f717ea5 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1507,6 +1507,18 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "838570988": { + "message": "Could not report token removal to the window token client.", + "level": "WARN", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowToken.java" + }, + "845234215": { + "message": "App is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "853091290": { "message": "Moved stack=%s behind stack=%s", "level": "DEBUG", diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 415092623531..75ea0cbada92 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -47,10 +47,10 @@ interface ILocationManager Location getLastLocation(in LocationRequest request, String packageName, String featureId); boolean getCurrentLocation(in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationListener listener, - String packageName, String featureId); + String packageName, String featureId, String listenerId); void requestLocationUpdates(in LocationRequest request, in ILocationListener listener, - in PendingIntent intent, String packageName, String featureId); + in PendingIntent intent, String packageName, String featureId, String listenerId); void removeUpdates(in ILocationListener listener, in PendingIntent intent); void requestGeofence(in LocationRequest request, in Geofence geofence, diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index fcbd3e540291..d1b41dfccf63 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -718,7 +718,7 @@ public class LocationManager { currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS); } - GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor, + GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor, consumer); if (cancellationSignal != null) { @@ -729,14 +729,15 @@ public class LocationManager { try { if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal, - listenerTransport, mContext.getPackageName(), mContext.getAttributionTag())) { - listenerTransport.register(mContext.getSystemService(AlarmManager.class), + transport, mContext.getPackageName(), mContext.getAttributionTag(), + transport.getListenerId())) { + transport.register(mContext.getSystemService(AlarmManager.class), remoteCancellationSignal); if (cancellationSignal != null) { - cancellationSignal.setOnCancelListener(listenerTransport::cancel); + cancellationSignal.setOnCancelListener(transport::cancel); } } else { - listenerTransport.fail(); + transport.fail(); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1175,7 +1176,8 @@ public class LocationManager { boolean registered = false; try { mService.requestLocationUpdates(locationRequest, transport, null, - mContext.getPackageName(), mContext.getAttributionTag()); + mContext.getPackageName(), mContext.getAttributionTag(), + transport.getListenerId()); registered = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1220,7 +1222,7 @@ public class LocationManager { try { mService.requestLocationUpdates(locationRequest, null, pendingIntent, - mContext.getPackageName(), mContext.getAttributionTag()); + mContext.getPackageName(), mContext.getAttributionTag(), null); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2558,6 +2560,10 @@ public class LocationManager { mRemoteCancellationSignal = null; } + public String getListenerId() { + return mConsumer.getClass().getName() + "@" + System.identityHashCode(mConsumer); + } + public synchronized void register(AlarmManager alarmManager, ICancellationSignal remoteCancellationSignal) { if (mConsumer == null) { @@ -2683,6 +2689,10 @@ public class LocationManager { return mListener; } + public String getListenerId() { + return mListener.getClass().getName() + "@" + System.identityHashCode(mListener); + } + public void register(@NonNull Executor executor) { Preconditions.checkArgument(executor != null, "invalid null executor"); mExecutor = executor; diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 4dd1a29d8595..5f0acc8f7647 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -264,8 +264,8 @@ public final class LocationRequest implements Parcelable { /* numUpdates= */ Integer.MAX_VALUE, /* smallestDisplacement= */ 0, /* hideFromAppOps= */ false, - /* lowPowerMode= */ false, /* locationSettingsIgnored= */ false, + /* lowPowerMode= */ false, /* workSource= */ null); } @@ -282,8 +282,8 @@ public final class LocationRequest implements Parcelable { src.mNumUpdates, src.mSmallestDisplacement, src.mHideFromAppOps, - src.mLowPowerMode, src.mLocationSettingsIgnored, + src.mLowPowerMode, src.mWorkSource); } diff --git a/packages/CarSystemUI/res/layout/notification_center_activity.xml b/packages/CarSystemUI/res/layout/notification_center_activity.xml index 4fef48918c97..0af74c4462a6 100644 --- a/packages/CarSystemUI/res/layout/notification_center_activity.xml +++ b/packages/CarSystemUI/res/layout/notification_center_activity.xml @@ -20,8 +20,6 @@ android:id="@+id/notification_view" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="invisible" - android:layout_marginBottom="@dimen/navigation_bar_height" android:background="@color/notification_shade_background_color"> <View diff --git a/packages/CarSystemUI/res/layout/notification_panel_container.xml b/packages/CarSystemUI/res/layout/notification_panel_container.xml index bf71396984b8..3b53c6aaeac3 100644 --- a/packages/CarSystemUI/res/layout/notification_panel_container.xml +++ b/packages/CarSystemUI/res/layout/notification_panel_container.xml @@ -18,4 +18,5 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/notification_container" android:layout_width="match_parent" - android:layout_height="match_parent"/> + android:layout_height="match_parent" + android:visibility="invisible"/> diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml index 1b0a211b733d..067e359e0e49 100644 --- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml +++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml @@ -22,10 +22,12 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + <!-- TODO(b/151617493): replace marginBottom with insets. --> <ViewStub android:id="@+id/notification_panel_stub" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout="@layout/notification_panel_container"/> + android:layout="@layout/notification_panel_container" + android:layout_marginBottom="@dimen/navigation_bar_height"/> <ViewStub android:id="@+id/fullscreen_user_switcher_stub" android:layout_width="match_parent" diff --git a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java index b057198d5177..44e43fe9af8c 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/CarDeviceProvisionedController.java @@ -24,7 +24,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedController; */ public interface CarDeviceProvisionedController extends DeviceProvisionedController { /** - * Returns {@code true} then SUW is in progress for the given user. + * Returns {@code true} when SUW is in progress for the given user. */ boolean isUserSetupInProgress(int user); diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java index 1901a2db879d..d8a894cfa8ba 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java @@ -16,9 +16,6 @@ package com.android.systemui.car.notification; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.app.ActivityManager; import android.car.Car; import android.car.drivingstate.CarUxRestrictionsManager; @@ -33,7 +30,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; @@ -54,7 +50,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.window.OverlayViewController; +import com.android.systemui.window.OverlayPanelViewController; import com.android.systemui.window.OverlayViewGlobalStateController; import javax.inject.Inject; @@ -62,39 +58,22 @@ import javax.inject.Singleton; /** View controller for the notification panel. */ @Singleton -public class NotificationPanelViewController extends OverlayViewController { - - // used to calculate how fast to open or close the window - private static final float DEFAULT_FLING_VELOCITY = 0; - // max time a fling animation takes - private static final float FLING_ANIMATION_MAX_TIME = 0.5f; - // acceleration rate for the fling animation - private static final float FLING_SPEED_UP_FACTOR = 0.6f; - - private static final int SWIPE_DOWN_MIN_DISTANCE = 25; - private static final int SWIPE_MAX_OFF_PATH = 75; - private static final int SWIPE_THRESHOLD_VELOCITY = 200; +public class NotificationPanelViewController extends OverlayPanelViewController { + private static final boolean DEBUG = true; private static final String TAG = "NotificationPanelViewController"; private final Context mContext; private final Resources mResources; private final CarServiceProvider mCarServiceProvider; - private final CarDeviceProvisionedController mCarDeviceProvisionedController; private final IStatusBarService mBarService; private final CommandQueue mCommandQueue; private final NotificationDataManager mNotificationDataManager; private final CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper; private final CarNotificationListener mCarNotificationListener; private final NotificationClickHandlerFactory mNotificationClickHandlerFactory; - private final FlingAnimationUtils mFlingAnimationUtils; private final StatusBarStateController mStatusBarStateController; - private final int mSettleClosePercentage; - - private float mOpeningVelocity = DEFAULT_FLING_VELOCITY; - private float mClosingVelocity = DEFAULT_FLING_VELOCITY; - private float mInitialBackgroundAlpha; private float mBackgroundAlphaDiff; @@ -108,13 +87,7 @@ public class NotificationPanelViewController extends OverlayViewController { private float mFirstTouchDownOnGlassPane; private boolean mNotificationListAtBottomAtTimeOfTouch; private boolean mIsSwipingVerticallyToClose; - private int mPercentageFromBottom; - private boolean mIsNotificationAnimating; private boolean mIsNotificationCardSwiping; - private boolean mPanelExpanded = false; - - private View.OnTouchListener mTopNavBarNotificationTouchListener; - private View.OnTouchListener mNavBarNotificationTouchListener; private OnUnseenCountUpdateListener mUnseenCountUpdateListener; @@ -123,6 +96,7 @@ public class NotificationPanelViewController extends OverlayViewController { Context context, @Main Resources resources, OverlayViewGlobalStateController overlayViewGlobalStateController, + FlingAnimationUtils.Builder flingAnimationUtilsBuilder, /* Other things */ CarServiceProvider carServiceProvider, @@ -135,26 +109,21 @@ public class NotificationPanelViewController extends OverlayViewController { CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper, CarNotificationListener carNotificationListener, NotificationClickHandlerFactory notificationClickHandlerFactory, - FlingAnimationUtils.Builder flingAnimationUtilsBuilder, /* Things that need to be replaced */ StatusBarStateController statusBarStateController ) { - super(R.id.notification_panel_stub, overlayViewGlobalStateController); + super(context, resources, R.id.notification_panel_stub, overlayViewGlobalStateController, + flingAnimationUtilsBuilder, carDeviceProvisionedController); mContext = context; mResources = resources; mCarServiceProvider = carServiceProvider; - mCarDeviceProvisionedController = carDeviceProvisionedController; mBarService = barService; mCommandQueue = commandQueue; mNotificationDataManager = notificationDataManager; mCarUxRestrictionManagerWrapper = carUxRestrictionManagerWrapper; mCarNotificationListener = carNotificationListener; mNotificationClickHandlerFactory = notificationClickHandlerFactory; - mFlingAnimationUtils = flingAnimationUtilsBuilder - .setMaxLengthSeconds(FLING_ANIMATION_MAX_TIME) - .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) - .build(); mStatusBarStateController = statusBarStateController; // Notification background setup. @@ -175,60 +144,6 @@ public class NotificationPanelViewController extends OverlayViewController { + " percentage"); } mBackgroundAlphaDiff = finalBackgroundAlpha - mInitialBackgroundAlpha; - - // Notification Panel param setup - mSettleClosePercentage = mResources.getInteger( - R.integer.notification_settle_close_percentage); - - // Attached to the top navigation bar (i.e. status bar) to detect pull down of the - // notification shade. - GestureDetector openGestureDetector = new GestureDetector(mContext, - new OpenNotificationGestureListener() { - @Override - protected void openNotification() { - animateExpandNotificationsPanel(); - } - }); - - // Attached to the NavBars to close the notification shade - GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(mContext, - new NavBarCloseNotificationGestureListener() { - @Override - protected void close() { - if (mPanelExpanded) { - animateCollapsePanels(); - } - } - }); - - mTopNavBarNotificationTouchListener = (v, event) -> { - if (!isInflated()) { - getOverlayViewGlobalStateController().inflateView(this); - } - if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) { - return true; - } - - boolean consumed = openGestureDetector.onTouchEvent(event); - if (consumed) { - return true; - } - maybeCompleteAnimation(event); - return true; - }; - - mNavBarNotificationTouchListener = - (v, event) -> { - if (!isInflated()) { - return true; - } - boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event); - if (consumed) { - return true; - } - maybeCompleteAnimation(event); - return true; - }; } @Override @@ -252,14 +167,13 @@ public class NotificationPanelViewController extends OverlayViewController { private void onNotificationViewInflated() { // Find views. mNotificationView = getLayout().findViewById(R.id.notification_view); - View glassPane = mNotificationView.findViewById(R.id.glass_pane); - mHandleBar = mNotificationView.findViewById(R.id.handle_bar); - mNotificationList = mNotificationView.findViewById(R.id.notifications); + setupHandleBar(); + setupNotificationPanel(); mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> { if (launchResult == ActivityManager.START_TASK_TO_FRONT || launchResult == ActivityManager.START_SUCCESS) { - animateCollapsePanels(); + animateCollapsePanel(); } }); @@ -269,39 +183,52 @@ public class NotificationPanelViewController extends OverlayViewController { mNotificationDataManager.getUnseenNotificationCount()); } }); + mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager); mNotificationView.setClickHandlerFactory(mNotificationClickHandlerFactory); mNotificationView.setNotificationDataManager(mNotificationDataManager); - mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { - super.onScrolled(recyclerView, dx, dy); - if (!mNotificationList.canScrollVertically(1)) { - mNotificationListAtBottom = true; - return; - } - mNotificationListAtBottom = false; - mIsSwipingVerticallyToClose = false; - mNotificationListAtBottomAtTimeOfTouch = false; - } + mCarServiceProvider.addListener(car -> { + CarUxRestrictionsManager carUxRestrictionsManager = + (CarUxRestrictionsManager) + car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); + mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager( + carUxRestrictionsManager); + + mNotificationViewController = new NotificationViewController( + mNotificationView, + PreprocessingManager.getInstance(mContext), + mCarNotificationListener, + mCarUxRestrictionManagerWrapper, + mNotificationDataManager); + mNotificationViewController.enable(); + }); + } + + private void setupHandleBar() { + mHandleBar = mNotificationView.findViewById(R.id.handle_bar); + GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext, + new HandleBarCloseGestureListener()); + mHandleBar.setOnTouchListener((v, event) -> { + handleBarCloseNotificationGestureDetector.onTouchEvent(event); + maybeCompleteAnimation(event); + return true; }); + } - // Attached to the notification ui to detect close request of the notification shade. + private void setupNotificationPanel() { + View glassPane = mNotificationView.findViewById(R.id.glass_pane); + mNotificationList = mNotificationView.findViewById(R.id.notifications); GestureDetector closeGestureDetector = new GestureDetector(mContext, - new CloseNotificationGestureListener() { + new CloseGestureListener() { @Override protected void close() { - if (mPanelExpanded) { - animateCollapsePanels(); + if (isPanelExpanded()) { + animateCollapsePanel(); } } }); - // Attached to the Handle bar to close the notification shade - GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext, - new HandleBarCloseNotificationGestureListener()); - // The glass pane is used to view touch events before passed to the notification list. // This allows us to initialize gesture listeners and detect when to close the notifications glassPane.setOnTouchListener((v, event) -> { @@ -320,6 +247,21 @@ public class NotificationPanelViewController extends OverlayViewController { return false; }); + mNotificationList.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + // Check if we can scroll vertically downwards. + if (!mNotificationList.canScrollVertically(/* direction= */ 1)) { + mNotificationListAtBottom = true; + return; + } + mNotificationListAtBottom = false; + mIsSwipingVerticallyToClose = false; + mNotificationListAtBottomAtTimeOfTouch = false; + } + }); + mNotificationList.setOnTouchListener((v, event) -> { mIsNotificationCardSwiping = Math.abs(mFirstTouchDownOnGlassPane - event.getRawX()) > SWIPE_MAX_OFF_PATH; @@ -341,19 +283,19 @@ public class NotificationPanelViewController extends OverlayViewController { boolean handled = closeGestureDetector.onTouchEvent(event); boolean isTracking = mIsTracking; - Rect rect = mNotificationView.getClipBounds(); + Rect rect = getLayout().getClipBounds(); float clippedHeight = 0; if (rect != null) { clippedHeight = rect.bottom; } if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP && mIsSwipingVerticallyToClose) { - if (mSettleClosePercentage < mPercentageFromBottom && isTracking) { - animateNotificationPanel(DEFAULT_FLING_VELOCITY, false); - } else if (clippedHeight != mNotificationView.getHeight() && isTracking) { + if (getSettleClosePercentage() < getPercentageFromBottom() && isTracking) { + animatePanel(DEFAULT_FLING_VELOCITY, false); + } else if (clippedHeight != getLayout().getHeight() && isTracking) { // this can be caused when user is at the end of the list and trying to // fling to top of the list by scrolling down. - animateNotificationPanel(DEFAULT_FLING_VELOCITY, true); + animatePanel(DEFAULT_FLING_VELOCITY, true); } } @@ -365,28 +307,6 @@ public class NotificationPanelViewController extends OverlayViewController { } return handled || isTracking; }); - - mCarServiceProvider.addListener(car -> { - CarUxRestrictionsManager carUxRestrictionsManager = - (CarUxRestrictionsManager) - car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE); - mCarUxRestrictionManagerWrapper.setCarUxRestrictionsManager( - carUxRestrictionsManager); - - mNotificationViewController = new NotificationViewController( - mNotificationView, - PreprocessingManager.getInstance(mContext), - mCarNotificationListener, - mCarUxRestrictionManagerWrapper, - mNotificationDataManager); - mNotificationViewController.enable(); - }); - - mHandleBar.setOnTouchListener((v, event) -> { - handleBarCloseNotificationGestureDetector.onTouchEvent(event); - maybeCompleteAnimation(event); - return true; - }); } /** Called when the car power state is changed to ON. */ @@ -397,139 +317,40 @@ public class NotificationPanelViewController extends OverlayViewController { mNotificationDataManager.clearAll(); } - View.OnTouchListener getTopNavBarNotificationTouchListener() { - return mTopNavBarNotificationTouchListener; - } - - View.OnTouchListener getNavBarNotificationTouchListener() { - return mNavBarNotificationTouchListener; - } - - private void maybeCompleteAnimation(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_UP - && mNotificationView.getVisibility() == View.VISIBLE) { - if (mSettleClosePercentage < mPercentageFromBottom) { - animateNotificationPanel(DEFAULT_FLING_VELOCITY, false); - } else { - animateNotificationPanel(DEFAULT_FLING_VELOCITY, true); - } - } + @Override + protected boolean shouldAnimateCollapsePanel() { + return true; } - /** - * Animates the notification shade from one position to other. This is used to either open or - * close the notification shade completely with a velocity. If the animation is to close the - * notification shade this method also makes the view invisible after animation ends. - */ - private void animateNotificationPanel(float velocity, boolean isClosing) { - float to = 0; - if (!isClosing) { - to = mNotificationView.getHeight(); - } - - Rect rect = mNotificationView.getClipBounds(); - if (rect != null && rect.bottom != to) { - float from = rect.bottom; - animate(from, to, velocity, isClosing); - return; - } - - // We will only be here if the shade is being opened programmatically or via button when - // height of the layout was not calculated. - ViewTreeObserver notificationTreeObserver = mNotificationView.getViewTreeObserver(); - notificationTreeObserver.addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - ViewTreeObserver obs = mNotificationView.getViewTreeObserver(); - obs.removeOnGlobalLayoutListener(this); - float to = mNotificationView.getHeight(); - animate(/* from= */ 0, to, velocity, isClosing); - } - }); + @Override + protected void onAnimateCollapsePanel() { + // No op. } - private void animateCollapsePanels() { - if (!mPanelExpanded || mNotificationView.getVisibility() == View.INVISIBLE) { - return; - } - getOverlayViewGlobalStateController().setWindowFocusable(false); - animateNotificationPanel(mClosingVelocity, true); + @Override + protected boolean shouldAnimateExpandPanel() { + return mCommandQueue.panelsEnabled(); } - private void animateExpandNotificationsPanel() { - if (!mCommandQueue.panelsEnabled() - || !mCarDeviceProvisionedController.isCurrentUserFullySetup()) { - return; - } - // scroll to top + @Override + protected void onAnimateExpandPanel() { mNotificationList.scrollToPosition(0); - setPanelVisible(true); - mNotificationView.setVisibility(View.VISIBLE); - animateNotificationPanel(mOpeningVelocity, false); - - setPanelExpanded(true); } - private void animate(float from, float to, float velocity, boolean isClosing) { - if (mIsNotificationAnimating) { - return; - } - mIsNotificationAnimating = true; - mIsTracking = true; - ValueAnimator animator = ValueAnimator.ofFloat(from, to); - animator.addUpdateListener( - animation -> { - float animatedValue = (Float) animation.getAnimatedValue(); - setNotificationViewClipBounds((int) animatedValue); - }); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - mIsNotificationAnimating = false; - mIsTracking = false; - mOpeningVelocity = DEFAULT_FLING_VELOCITY; - mClosingVelocity = DEFAULT_FLING_VELOCITY; - if (isClosing) { - setPanelVisible(false); - mNotificationView.setVisibility(View.INVISIBLE); - mNotificationView.setClipBounds(null); - mNotificationViewController.onVisibilityChanged(false); - // let the status bar know that the panel is closed - setPanelExpanded(false); - } else { - mNotificationViewController.onVisibilityChanged(true); - // let the status bar know that the panel is open - mNotificationView.setVisibleNotificationsAsSeen(); - setPanelExpanded(true); - } - } - }); - mFlingAnimationUtils.apply(animator, from, to, Math.abs(velocity)); - animator.start(); + @Override + protected void onCollapseAnimationEnd() { + mNotificationViewController.onVisibilityChanged(false); } - /** - * Set the panel view to be visible. - */ - public void setPanelVisible(boolean visible) { - if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) { - getOverlayViewGlobalStateController().setWindowVisible(true); - } - if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) { - getOverlayViewGlobalStateController().setWindowVisible(false); - } - getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - getOverlayViewGlobalStateController().setWindowFocusable(visible); + @Override + protected void onExpandAnimationEnd() { + mNotificationViewController.onVisibilityChanged(true); + mNotificationView.setVisibleNotificationsAsSeen(); } - /** - * Set the panel state to expanded. This will expand or collapse the overlay window if - * necessary. - */ - public void setPanelExpanded(boolean expand) { - mPanelExpanded = expand; + @Override + protected void onPanelExpanded(boolean expand) { + super.onPanelExpanded(expand); if (expand && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { if (DEBUG) { @@ -550,19 +371,19 @@ public class NotificationPanelViewController extends OverlayViewController { } } - private void setNotificationViewClipBounds(int height) { - if (height > mNotificationView.getHeight()) { - height = mNotificationView.getHeight(); - } - Rect clipBounds = new Rect(); - clipBounds.set(0, 0, mNotificationView.getWidth(), height); - // Sets the clip region on the notification list view. - mNotificationView.setClipBounds(clipBounds); + @Override + protected void onOpenScrollStart() { + mNotificationList.scrollToPosition(0); + } + + @Override + protected void onScroll(int height) { if (mHandleBar != null) { ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mHandleBar.getLayoutParams(); mHandleBar.setTranslationY(height - mHandleBar.getHeight() - lp.bottomMargin); } + if (mNotificationView.getHeight() > 0) { Drawable background = mNotificationView.getBackground().mutate(); background.setAlpha((int) (getBackgroundAlpha(height) * 255)); @@ -570,6 +391,13 @@ public class NotificationPanelViewController extends OverlayViewController { } } + @Override + protected boolean shouldAllowClosingScroll() { + // Unless the notification list is at the bottom, the panel shouldn't be allowed to + // collapse on scroll. + return mNotificationListAtBottomAtTimeOfTouch; + } + /** * Calculates the alpha value for the background based on how much of the notification * shade is visible to the user. When the notification shade is completely open then @@ -580,30 +408,6 @@ public class NotificationPanelViewController extends OverlayViewController { + ((float) height / mNotificationView.getHeight() * mBackgroundAlphaDiff); } - private void calculatePercentageFromBottom(float height) { - if (mNotificationView.getHeight() > 0) { - mPercentageFromBottom = (int) Math.abs( - height / mNotificationView.getHeight() * 100); - } - } - - /** Toggles the visibility of the notification panel. */ - public void toggle() { - if (!isInflated()) { - getOverlayViewGlobalStateController().inflateView(this); - } - if (mPanelExpanded) { - animateCollapsePanels(); - } else { - animateExpandNotificationsPanel(); - } - } - - /** Returns {@code true} if the notification panel is expanded. */ - public boolean isPanelExpanded() { - return mPanelExpanded; - } - /** Sets the unseen count listener. */ public void setOnUnseenCountUpdateListener(OnUnseenCountUpdateListener listener) { mUnseenCountUpdateListener = listener; @@ -619,154 +423,9 @@ public class NotificationPanelViewController extends OverlayViewController { } /** - * Only responsible for open hooks. Since once the panel opens it covers all elements - * there is no need to merge with close. - */ - private abstract class OpenNotificationGestureListener extends - GestureDetector.SimpleOnGestureListener { - - @Override - public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, - float distanceY) { - - if (mNotificationView.getVisibility() == View.INVISIBLE) { - // when the on-scroll is called for the first time to open. - mNotificationList.scrollToPosition(0); - } - setPanelVisible(true); - mNotificationView.setVisibility(View.VISIBLE); - - // clips the view for the notification shade when the user scrolls to open. - setNotificationViewClipBounds((int) event2.getRawY()); - - // Initially the scroll starts with height being zero. This checks protects from divide - // by zero error. - calculatePercentageFromBottom(event2.getRawY()); - - mIsTracking = true; - return true; - } - - - @Override - public boolean onFling(MotionEvent event1, MotionEvent event2, - float velocityX, float velocityY) { - if (velocityY > SWIPE_THRESHOLD_VELOCITY) { - mOpeningVelocity = velocityY; - openNotification(); - return true; - } - animateNotificationPanel(DEFAULT_FLING_VELOCITY, true); - - return false; - } - - protected abstract void openNotification(); - } - - /** - * To be installed on the open panel notification panel - */ - private abstract class CloseNotificationGestureListener extends - GestureDetector.SimpleOnGestureListener { - - @Override - public boolean onSingleTapUp(MotionEvent motionEvent) { - if (mPanelExpanded) { - animateNotificationPanel(DEFAULT_FLING_VELOCITY, true); - } - return true; - } - - @Override - public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, - float distanceY) { - // should not clip while scroll to the bottom of the list. - if (!mNotificationListAtBottomAtTimeOfTouch) { - return false; - } - float actualNotificationHeight = - mNotificationView.getHeight() - (event1.getRawY() - event2.getRawY()); - if (actualNotificationHeight > mNotificationView.getHeight()) { - actualNotificationHeight = mNotificationView.getHeight(); - } - if (mNotificationView.getHeight() > 0) { - mPercentageFromBottom = (int) Math.abs( - actualNotificationHeight / mNotificationView.getHeight() * 100); - boolean isUp = distanceY > 0; - - // This check is to figure out if onScroll was called while swiping the card at - // bottom of the list. At that time we should not allow notification shade to - // close. We are also checking for the upwards swipe gesture here because it is - // possible if a user is closing the notification shade and while swiping starts - // to open again but does not fling. At that time we should allow the - // notification shade to close fully or else it would stuck in between. - if (Math.abs(mNotificationView.getHeight() - actualNotificationHeight) - > SWIPE_DOWN_MIN_DISTANCE && isUp) { - setNotificationViewClipBounds((int) actualNotificationHeight); - mIsTracking = true; - } else if (!isUp) { - setNotificationViewClipBounds((int) actualNotificationHeight); - } - } - // if we return true the items in RV won't be scrollable. - return false; - } - - - @Override - public boolean onFling(MotionEvent event1, MotionEvent event2, - float velocityX, float velocityY) { - // should not fling if the touch does not start when view is at the bottom of the list. - if (!mNotificationListAtBottomAtTimeOfTouch) { - return false; - } - if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH - || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) { - // swipe was not vertical or was not fast enough - return false; - } - boolean isUp = velocityY < 0; - if (isUp) { - close(); - return true; - } else { - // we should close the shade - animateNotificationPanel(velocityY, false); - } - return false; - } - - protected abstract void close(); - } - - /** - * To be installed on the nav bars. - */ - private abstract class NavBarCloseNotificationGestureListener extends - CloseNotificationGestureListener { - @Override - public boolean onSingleTapUp(MotionEvent e) { - mClosingVelocity = DEFAULT_FLING_VELOCITY; - if (mPanelExpanded) { - close(); - } - return super.onSingleTapUp(e); - } - - @Override - public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, - float distanceY) { - calculatePercentageFromBottom(event2.getRawY()); - setNotificationViewClipBounds((int) event2.getRawY()); - return true; - } - } - - /** * To be installed on the handle bar. */ - private class HandleBarCloseNotificationGestureListener extends + private class HandleBarCloseGestureListener extends GestureDetector.SimpleOnGestureListener { @Override @@ -777,9 +436,8 @@ public class NotificationPanelViewController extends OverlayViewController { // the handle bar we should calculate the height using the diff of event1 and event2. // This will help the notification shade to clip smoothly as the event2 value changes // as event1 value will be fixed. - int clipHeight = - mNotificationView.getHeight() - (int) (event1.getRawY() - event2.getRawY()); - setNotificationViewClipBounds(clipHeight); + int clipHeight = getLayout().getHeight() - (int) (event1.getRawY() - event2.getRawY()); + setViewClipBounds(clipHeight); return true; } } diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java index 110c2ee8854b..9d71797794b8 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewMediator.java @@ -59,13 +59,13 @@ public class NotificationPanelViewMediator implements OverlayViewMediator, @Override public void registerListeners() { mCarNavigationBarController.registerTopBarTouchListener( - mNotificationPanelViewController.getTopNavBarNotificationTouchListener()); + mNotificationPanelViewController.getDragOpenTouchListener()); mCarNavigationBarController.registerBottomBarTouchListener( - mNotificationPanelViewController.getNavBarNotificationTouchListener()); + mNotificationPanelViewController.getDragCloseTouchListener()); mCarNavigationBarController.registerLeftBarTouchListener( - mNotificationPanelViewController.getNavBarNotificationTouchListener()); + mNotificationPanelViewController.getDragCloseTouchListener()); mCarNavigationBarController.registerRightBarTouchListener( - mNotificationPanelViewController.getNavBarNotificationTouchListener()); + mNotificationPanelViewController.getDragCloseTouchListener()); mCarNavigationBarController.registerNotificationController( new CarNavigationBarController.NotificationsShadeController() { diff --git a/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java new file mode 100644 index 000000000000..58022f12e58c --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/window/OverlayPanelViewController.java @@ -0,0 +1,563 @@ +/* + * 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.window; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Rect; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewTreeObserver; + +import androidx.annotation.CallSuper; + +import com.android.systemui.R; +import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.FlingAnimationUtils; + +/** + * The {@link OverlayPanelViewController} provides additional dragging animation capabilities to + * {@link OverlayViewController}. + */ +public abstract class OverlayPanelViewController extends OverlayViewController { + + private static final boolean DEBUG = true; + private static final String TAG = "OverlayPanelViewController"; + + // used to calculate how fast to open or close the window + protected static final float DEFAULT_FLING_VELOCITY = 0; + // max time a fling animation takes + protected static final float FLING_ANIMATION_MAX_TIME = 0.5f; + // acceleration rate for the fling animation + protected static final float FLING_SPEED_UP_FACTOR = 0.6f; + + protected static final int SWIPE_DOWN_MIN_DISTANCE = 25; + protected static final int SWIPE_MAX_OFF_PATH = 75; + protected static final int SWIPE_THRESHOLD_VELOCITY = 200; + + private final FlingAnimationUtils mFlingAnimationUtils; + private final CarDeviceProvisionedController mCarDeviceProvisionedController; + private final View.OnTouchListener mDragOpenTouchListener; + private final View.OnTouchListener mDragCloseTouchListener; + + private final int mSettleClosePercentage; + private int mPercentageFromBottom; + + private boolean mPanelVisible; + private boolean mPanelExpanded; + + private float mOpeningVelocity = DEFAULT_FLING_VELOCITY; + private float mClosingVelocity = DEFAULT_FLING_VELOCITY; + + private boolean mIsAnimating; + private boolean mIsTracking; + + public OverlayPanelViewController( + Context context, + @Main Resources resources, + int stubId, + OverlayViewGlobalStateController overlayViewGlobalStateController, + FlingAnimationUtils.Builder flingAnimationUtilsBuilder, + CarDeviceProvisionedController carDeviceProvisionedController + ) { + super(stubId, overlayViewGlobalStateController); + + mFlingAnimationUtils = flingAnimationUtilsBuilder + .setMaxLengthSeconds(FLING_ANIMATION_MAX_TIME) + .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) + .build(); + mCarDeviceProvisionedController = carDeviceProvisionedController; + + mSettleClosePercentage = resources.getInteger( + R.integer.notification_settle_close_percentage); + + // Attached to the top navigation bar (i.e. status bar) to detect pull down of the + // notification shade. + GestureDetector openGestureDetector = new GestureDetector(context, + new OpenGestureListener() { + @Override + protected void open() { + animateExpandPanel(); + } + }); + + // Attached to the NavBars to close the notification shade + GestureDetector navBarCloseNotificationGestureDetector = new GestureDetector(context, + new SystemBarCloseGestureListener() { + @Override + protected void close() { + if (isPanelExpanded()) { + animateCollapsePanel(); + } + } + }); + + mDragOpenTouchListener = (v, event) -> { + if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) { + return true; + } + if (!isInflated()) { + getOverlayViewGlobalStateController().inflateView(this); + } + + boolean consumed = openGestureDetector.onTouchEvent(event); + if (consumed) { + return true; + } + maybeCompleteAnimation(event); + return true; + }; + + mDragCloseTouchListener = (v, event) -> { + if (!isInflated()) { + return true; + } + boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event); + if (consumed) { + return true; + } + maybeCompleteAnimation(event); + return true; + }; + } + + /** Toggles the visibility of the panel. */ + public void toggle() { + if (!isInflated()) { + getOverlayViewGlobalStateController().inflateView(this); + } + if (isPanelExpanded()) { + animateCollapsePanel(); + } else { + animateExpandPanel(); + } + } + + /* ***************************************************************************************** * + * Panel Animation + * ***************************************************************************************** */ + + /** Animates the closing of the panel. */ + protected void animateCollapsePanel() { + if (!shouldAnimateCollapsePanel()) { + return; + } + + if (!isPanelExpanded() || !isPanelVisible()) { + return; + } + + onAnimateCollapsePanel(); + getOverlayViewGlobalStateController().setWindowFocusable(false); + animatePanel(mClosingVelocity, /* isClosing= */ true); + } + + /** Determines whether {@link #animateCollapsePanel()} should collapse the panel. */ + protected abstract boolean shouldAnimateCollapsePanel(); + + /** Called when the panel is beginning to collapse. */ + protected abstract void onAnimateCollapsePanel(); + + /** Animates the expansion of the panel. */ + protected void animateExpandPanel() { + if (!shouldAnimateExpandPanel()) { + return; + } + + if (!mCarDeviceProvisionedController.isCurrentUserFullySetup()) { + return; + } + + onAnimateExpandPanel(); + setPanelVisible(true); + animatePanel(mOpeningVelocity, /* isClosing= */ false); + + setPanelExpanded(true); + } + + /** Determines whether {@link #animateExpandPanel()}} should expand the panel. */ + protected abstract boolean shouldAnimateExpandPanel(); + + /** Called when the panel is beginning to expand. */ + protected abstract void onAnimateExpandPanel(); + + /** + * Depending on certain conditions, determines whether to fully expand or collapse the panel. + */ + protected void maybeCompleteAnimation(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_UP + && isPanelVisible()) { + if (mSettleClosePercentage < mPercentageFromBottom) { + animatePanel(DEFAULT_FLING_VELOCITY, false); + } else { + animatePanel(DEFAULT_FLING_VELOCITY, true); + } + } + } + + /** + * Animates the panel from one position to other. This is used to either open or + * close the panel completely with a velocity. If the animation is to close the + * panel this method also makes the view invisible after animation ends. + */ + protected void animatePanel(float velocity, boolean isClosing) { + float to = 0; + if (!isClosing) { + to = getLayout().getHeight(); + } + + Rect rect = getLayout().getClipBounds(); + if (rect != null && rect.bottom != to) { + float from = rect.bottom; + animate(from, to, velocity, isClosing); + return; + } + + // We will only be here if the shade is being opened programmatically or via button when + // height of the layout was not calculated. + ViewTreeObserver notificationTreeObserver = getLayout().getViewTreeObserver(); + notificationTreeObserver.addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + ViewTreeObserver obs = getLayout().getViewTreeObserver(); + obs.removeOnGlobalLayoutListener(this); + float to = getLayout().getHeight(); + animate(/* from= */ 0, to, velocity, isClosing); + } + }); + } + + private void animate(float from, float to, float velocity, boolean isClosing) { + if (mIsAnimating) { + return; + } + mIsAnimating = true; + mIsTracking = true; + ValueAnimator animator = ValueAnimator.ofFloat(from, to); + animator.addUpdateListener( + animation -> { + float animatedValue = (Float) animation.getAnimatedValue(); + setViewClipBounds((int) animatedValue); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mIsAnimating = false; + mIsTracking = false; + mOpeningVelocity = DEFAULT_FLING_VELOCITY; + mClosingVelocity = DEFAULT_FLING_VELOCITY; + if (isClosing) { + setPanelVisible(false); + getLayout().setClipBounds(null); + onCollapseAnimationEnd(); + setPanelExpanded(false); + } else { + onExpandAnimationEnd(); + setPanelExpanded(true); + } + } + }); + getFlingAnimationUtils().apply(animator, from, to, Math.abs(velocity)); + animator.start(); + } + + /** + * Called in {@link Animator.AnimatorListener#onAnimationEnd(Animator)} when the panel is + * closing. + */ + protected abstract void onCollapseAnimationEnd(); + + /** + * Called in {@link Animator.AnimatorListener#onAnimationEnd(Animator)} when the panel is + * opening. + */ + protected abstract void onExpandAnimationEnd(); + + /* ***************************************************************************************** * + * Panel Visibility + * ***************************************************************************************** */ + + /** Set the panel view to be visible. */ + protected final void setPanelVisible(boolean visible) { + mPanelVisible = visible; + onPanelVisible(visible); + } + + /** Returns {@code true} if panel is visible. */ + public final boolean isPanelVisible() { + return mPanelVisible; + } + + /** Business logic run when panel visibility is set. */ + @CallSuper + protected void onPanelVisible(boolean visible) { + if (DEBUG) { + Log.e(TAG, "onPanelVisible: " + visible); + } + + if (visible && !getOverlayViewGlobalStateController().isWindowVisible()) { + getOverlayViewGlobalStateController().setWindowVisible(true); + } + if (!visible && getOverlayViewGlobalStateController().isWindowVisible()) { + getOverlayViewGlobalStateController().setWindowVisible(false); + } + getLayout().setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + getOverlayViewGlobalStateController().setWindowFocusable(visible); + } + + /* ***************************************************************************************** * + * Panel Expansion + * ***************************************************************************************** */ + + /** + * Set the panel state to expanded. This will expand or collapse the overlay window if + * necessary. + */ + protected final void setPanelExpanded(boolean expand) { + mPanelExpanded = expand; + onPanelExpanded(expand); + } + + /** Returns {@code true} if panel is expanded. */ + public final boolean isPanelExpanded() { + return mPanelExpanded; + } + + @CallSuper + protected void onPanelExpanded(boolean expand) { + if (DEBUG) { + Log.e(TAG, "onPanelExpanded: " + expand); + } + } + + /* ***************************************************************************************** * + * Misc + * ***************************************************************************************** */ + + protected void calculatePercentageFromBottom(float height) { + if (getLayout().getHeight() > 0) { + mPercentageFromBottom = (int) Math.abs( + height / getLayout().getHeight() * 100); + } + } + + protected void setViewClipBounds(int height) { + if (height > getLayout().getHeight()) { + height = getLayout().getHeight(); + } + Rect clipBounds = new Rect(); + clipBounds.set(0, 0, getLayout().getWidth(), height); + getLayout().setClipBounds(clipBounds); + onScroll(height); + } + + /** Called while scrolling. */ + protected abstract void onScroll(int height); + + /* ***************************************************************************************** * + * Getters + * ***************************************************************************************** */ + + /** Returns the open touch listener. */ + public final View.OnTouchListener getDragOpenTouchListener() { + return mDragOpenTouchListener; + } + + /** Returns the close touch listener. */ + public final View.OnTouchListener getDragCloseTouchListener() { + return mDragCloseTouchListener; + } + + /** Gets the fling animation utils used for animating this panel. */ + protected final FlingAnimationUtils getFlingAnimationUtils() { + return mFlingAnimationUtils; + } + + /** Returns {@code true} if the panel is currently tracking. */ + protected final boolean isTracking() { + return mIsTracking; + } + + /** Returns {@code true} if the panel is currently animating. */ + protected final boolean isAnimating() { + return mIsAnimating; + } + + /** Returns the percentage of the panel that is open from the bottom. */ + protected final int getPercentageFromBottom() { + return mPercentageFromBottom; + } + + /** Returns the percentage at which we've determined whether to open or close the panel. */ + protected final int getSettleClosePercentage() { + return mSettleClosePercentage; + } + + /* ***************************************************************************************** * + * Gesture Listeners + * ***************************************************************************************** */ + + /** Called when the user is beginning to scroll down the panel. */ + protected abstract void onOpenScrollStart(); + + /** + * Only responsible for open hooks. Since once the panel opens it covers all elements + * there is no need to merge with close. + */ + protected abstract class OpenGestureListener extends + GestureDetector.SimpleOnGestureListener { + + @Override + public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, + float distanceY) { + + if (!isPanelVisible()) { + onOpenScrollStart(); + } + setPanelVisible(true); + + // clips the view for the notification shade when the user scrolls to open. + setViewClipBounds((int) event2.getRawY()); + + // Initially the scroll starts with height being zero. This checks protects from divide + // by zero error. + calculatePercentageFromBottom(event2.getRawY()); + + mIsTracking = true; + return true; + } + + + @Override + public boolean onFling(MotionEvent event1, MotionEvent event2, + float velocityX, float velocityY) { + if (velocityY > SWIPE_THRESHOLD_VELOCITY) { + mOpeningVelocity = velocityY; + open(); + return true; + } + animatePanel(DEFAULT_FLING_VELOCITY, true); + + return false; + } + + protected abstract void open(); + } + + /** Determines whether the scroll event should allow closing of the panel. */ + protected abstract boolean shouldAllowClosingScroll(); + + protected abstract class CloseGestureListener extends + GestureDetector.SimpleOnGestureListener { + + @Override + public boolean onSingleTapUp(MotionEvent motionEvent) { + if (isPanelExpanded()) { + animatePanel(DEFAULT_FLING_VELOCITY, true); + } + return true; + } + + @Override + public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, + float distanceY) { + // should not clip while scroll to the bottom of the list. + if (!shouldAllowClosingScroll()) { + return false; + } + float actualNotificationHeight = + getLayout().getHeight() - (event1.getRawY() - event2.getRawY()); + if (actualNotificationHeight > getLayout().getHeight()) { + actualNotificationHeight = getLayout().getHeight(); + } + if (getLayout().getHeight() > 0) { + mPercentageFromBottom = (int) Math.abs( + actualNotificationHeight / getLayout().getHeight() * 100); + boolean isUp = distanceY > 0; + + // This check is to figure out if onScroll was called while swiping the card at + // bottom of the list. At that time we should not allow notification shade to + // close. We are also checking for the upwards swipe gesture here because it is + // possible if a user is closing the notification shade and while swiping starts + // to open again but does not fling. At that time we should allow the + // notification shade to close fully or else it would stuck in between. + if (Math.abs(getLayout().getHeight() - actualNotificationHeight) + > SWIPE_DOWN_MIN_DISTANCE && isUp) { + setViewClipBounds((int) actualNotificationHeight); + mIsTracking = true; + } else if (!isUp) { + setViewClipBounds((int) actualNotificationHeight); + } + } + // if we return true the items in RV won't be scrollable. + return false; + } + + + @Override + public boolean onFling(MotionEvent event1, MotionEvent event2, + float velocityX, float velocityY) { + // should not fling if the touch does not start when view is at the bottom of the list. + if (!shouldAllowClosingScroll()) { + return false; + } + if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH + || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) { + // swipe was not vertical or was not fast enough + return false; + } + boolean isUp = velocityY < 0; + if (isUp) { + close(); + return true; + } else { + // we should close the shade + animatePanel(velocityY, false); + } + return false; + } + + protected abstract void close(); + } + + protected abstract class SystemBarCloseGestureListener extends CloseGestureListener { + @Override + public boolean onSingleTapUp(MotionEvent e) { + mClosingVelocity = DEFAULT_FLING_VELOCITY; + if (isPanelExpanded()) { + close(); + } + return super.onSingleTapUp(e); + } + + @Override + public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, + float distanceY) { + calculatePercentageFromBottom(event2.getRawY()); + setViewClipBounds((int) event2.getRawY()); + return true; + } + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java index f2748b89c95c..76557fda6926 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarTest.java @@ -20,13 +20,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.Handler; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; import android.view.LayoutInflater; import android.view.WindowManager; +import androidx.test.filters.SmallTest; + import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.car.CarDeviceProvisionedController; diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java new file mode 100644 index 000000000000..04f2d06ca71c --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayPanelViewControllerTest.java @@ -0,0 +1,431 @@ +/* + * 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.window; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarDeviceProvisionedController; +import com.android.systemui.statusbar.FlingAnimationUtils; +import com.android.systemui.tests.R; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class OverlayPanelViewControllerTest extends SysuiTestCase { + private TestOverlayPanelViewController mOverlayPanelViewController; + private ViewGroup mBaseLayout; + + @Mock + private OverlayViewGlobalStateController mOverlayViewGlobalStateController; + @Mock + private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + @Mock + private FlingAnimationUtils mFlingAnimationUtils; + @Mock + private CarDeviceProvisionedController mCarDeviceProvisionedController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate( + R.layout.overlay_view_controller_test, /* root= */ null); + + when(mFlingAnimationUtilsBuilder.setMaxLengthSeconds(anyFloat())).thenReturn( + mFlingAnimationUtilsBuilder); + when(mFlingAnimationUtilsBuilder.setSpeedUpFactor(anyFloat())).thenReturn( + mFlingAnimationUtilsBuilder); + when(mFlingAnimationUtilsBuilder.build()).thenReturn(mFlingAnimationUtils); + mOverlayPanelViewController = new TestOverlayPanelViewController( + getContext(), + getContext().getOrCreateTestableResources().getResources(), + R.id.overlay_view_controller_stub, + mOverlayViewGlobalStateController, + mFlingAnimationUtilsBuilder, + mCarDeviceProvisionedController); + } + + @Test + public void toggle_notInflated_inflates() { + assertThat(mOverlayPanelViewController.isInflated()).isFalse(); + + mOverlayPanelViewController.toggle(); + + verify(mOverlayViewGlobalStateController).inflateView(mOverlayPanelViewController); + } + + @Test + public void toggle_inflated_doesNotInflate() { + mOverlayPanelViewController.inflate(mBaseLayout); + assertThat(mOverlayPanelViewController.isInflated()).isTrue(); + + mOverlayPanelViewController.toggle(); + + verify(mOverlayViewGlobalStateController, never()).inflateView(mOverlayPanelViewController); + } + + @Test + public void toggle_notExpanded_panelExpands() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setPanelExpanded(false); + + mOverlayPanelViewController.toggle(); + + assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue(); + } + + @Test + public void toggle_expanded_panelCollapses() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setPanelExpanded(true); + + mOverlayPanelViewController.toggle(); + + assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue(); + } + + @Test + public void animateCollapsePanel_shouldNotAnimateCollapsePanel_doesNotCollapse() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateCollapsePanel(false); + + mOverlayPanelViewController.animateCollapsePanel(); + + assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue(); + assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse(); + } + + @Test + public void animateCollapsePanel_isNotExpanded_doesNotCollapse() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateCollapsePanel(true); + mOverlayPanelViewController.setPanelExpanded(false); + + mOverlayPanelViewController.animateCollapsePanel(); + + assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue(); + assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse(); + } + + @Test + public void animateCollapsePanel_isNotVisible_doesNotCollapse() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateCollapsePanel(true); + mOverlayPanelViewController.setPanelExpanded(true); + mOverlayPanelViewController.setPanelVisible(false); + + mOverlayPanelViewController.animateCollapsePanel(); + + assertThat(mOverlayPanelViewController.mAnimateCollapsePanelCalled).isTrue(); + assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isFalse(); + } + + @Test + public void animateCollapsePanel_collapses() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateCollapsePanel(true); + mOverlayPanelViewController.setPanelExpanded(true); + mOverlayPanelViewController.setPanelVisible(true); + + mOverlayPanelViewController.animateCollapsePanel(); + + assertThat(mOverlayPanelViewController.mOnAnimateCollapsePanelCalled).isTrue(); + } + + @Test + public void animateCollapsePanel_removesWindowFocus() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateCollapsePanel(true); + mOverlayPanelViewController.setPanelExpanded(true); + mOverlayPanelViewController.setPanelVisible(true); + + mOverlayPanelViewController.animateCollapsePanel(); + + verify(mOverlayViewGlobalStateController).setWindowFocusable(false); + } + + @Test + public void animateExpandPanel_shouldNotAnimateExpandPanel_doesNotExpand() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateExpandPanel(false); + + mOverlayPanelViewController.animateExpandPanel(); + + assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue(); + assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isFalse(); + } + + @Test + public void animateExpandPanel_userNotSetup_doesNotExpand() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateExpandPanel(true); + when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(false); + + mOverlayPanelViewController.animateExpandPanel(); + + assertThat(mOverlayPanelViewController.mAnimateExpandPanelCalled).isTrue(); + assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isFalse(); + } + + @Test + public void animateExpandPanel_expands() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateExpandPanel(true); + when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true); + + mOverlayPanelViewController.animateExpandPanel(); + + assertThat(mOverlayPanelViewController.mOnAnimateExpandPanelCalled).isTrue(); + } + + @Test + public void animateExpandPanel_setsPanelVisible() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateExpandPanel(true); + when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true); + + mOverlayPanelViewController.animateExpandPanel(); + + assertThat(mOverlayPanelViewController.isPanelVisible()).isTrue(); + } + + @Test + public void animateExpandPanel_setsPanelExpanded() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setShouldAnimateExpandPanel(true); + when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true); + + mOverlayPanelViewController.animateExpandPanel(); + + assertThat(mOverlayPanelViewController.isPanelExpanded()).isTrue(); + } + + @Test + public void setPanelVisible_setTrue_windowNotVisible_setsWindowVisible() { + mOverlayPanelViewController.inflate(mBaseLayout); + when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(false); + + mOverlayPanelViewController.setPanelVisible(true); + + verify(mOverlayViewGlobalStateController).setWindowVisible(true); + } + + @Test + public void setPanelVisible_setTrue_windowVisible_doesNotSetWindowVisible() { + mOverlayPanelViewController.inflate(mBaseLayout); + when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true); + + mOverlayPanelViewController.setPanelVisible(true); + + verify(mOverlayViewGlobalStateController, never()).setWindowVisible(true); + } + + @Test + public void setPanelVisible_setTrue_setLayoutVisible() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.getLayout().setVisibility(View.INVISIBLE); + + mOverlayPanelViewController.setPanelVisible(true); + + assertThat(mOverlayPanelViewController.getLayout().getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void setPanelVisible_setTrue_setWindowFocusable() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.setPanelVisible(true); + + verify(mOverlayViewGlobalStateController).setWindowFocusable(true); + } + + @Test + public void setPanelVisible_setFalse_windowVisible_setsWindowNotVisible() { + mOverlayPanelViewController.inflate(mBaseLayout); + when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(true); + + mOverlayPanelViewController.setPanelVisible(false); + + verify(mOverlayViewGlobalStateController).setWindowVisible(false); + } + + @Test + public void setPanelVisible_setFalse_windowNotVisible_doesNotSetWindowNotVisible() { + mOverlayPanelViewController.inflate(mBaseLayout); + when(mOverlayViewGlobalStateController.isWindowVisible()).thenReturn(false); + + mOverlayPanelViewController.setPanelVisible(false); + + verify(mOverlayViewGlobalStateController, never()).setWindowVisible(false); + } + + @Test + public void setPanelVisible_setFalse_setLayoutInvisible() { + mOverlayPanelViewController.inflate(mBaseLayout); + mOverlayPanelViewController.getLayout().setVisibility(View.VISIBLE); + + mOverlayPanelViewController.setPanelVisible(false); + + assertThat(mOverlayPanelViewController.getLayout().getVisibility()).isEqualTo( + View.INVISIBLE); + } + + @Test + public void setPanelVisible_setFalse_setWindowNotFocusable() { + mOverlayPanelViewController.inflate(mBaseLayout); + + mOverlayPanelViewController.setPanelVisible(false); + + verify(mOverlayViewGlobalStateController).setWindowFocusable(false); + } + + @Test + public void dragOpenTouchListener_isNotInflated_inflatesView() { + when(mCarDeviceProvisionedController.isCurrentUserFullySetup()).thenReturn(true); + assertThat(mOverlayPanelViewController.isInflated()).isFalse(); + + mOverlayPanelViewController.getDragOpenTouchListener().onTouch(/* v= */ null, + MotionEvent.obtain(/* downTime= */ 200, /* eventTime= */ 300, + MotionEvent.ACTION_MOVE, /* x= */ 0, /* y= */ 0, /* metaState= */ 0)); + + verify(mOverlayViewGlobalStateController).inflateView(mOverlayPanelViewController); + } + + private static class TestOverlayPanelViewController extends OverlayPanelViewController { + + private boolean mShouldAnimateCollapsePanel; + private boolean mShouldAnimateExpandPanel; + private boolean mShouldAllowClosingScroll; + + boolean mOnAnimateCollapsePanelCalled; + boolean mAnimateCollapsePanelCalled; + boolean mOnAnimateExpandPanelCalled; + boolean mAnimateExpandPanelCalled; + boolean mOnCollapseAnimationEndCalled; + boolean mOnExpandAnimationEndCalled; + boolean mOnOpenScrollStartEnd; + List<Integer> mOnScrollHeights; + + TestOverlayPanelViewController( + Context context, + Resources resources, + int stubId, + OverlayViewGlobalStateController overlayViewGlobalStateController, + FlingAnimationUtils.Builder flingAnimationUtilsBuilder, + CarDeviceProvisionedController carDeviceProvisionedController) { + super(context, resources, stubId, overlayViewGlobalStateController, + flingAnimationUtilsBuilder, + carDeviceProvisionedController); + + mOnScrollHeights = new ArrayList<>(); + } + + public void setShouldAnimateCollapsePanel(boolean shouldAnimate) { + mShouldAnimateCollapsePanel = shouldAnimate; + } + + @Override + protected boolean shouldAnimateCollapsePanel() { + return mShouldAnimateCollapsePanel; + } + + @Override + protected void animateCollapsePanel() { + super.animateCollapsePanel(); + mAnimateCollapsePanelCalled = true; + } + + @Override + protected void onAnimateCollapsePanel() { + mOnAnimateCollapsePanelCalled = true; + } + + public void setShouldAnimateExpandPanel(boolean shouldAnimate) { + mShouldAnimateExpandPanel = shouldAnimate; + } + + @Override + protected boolean shouldAnimateExpandPanel() { + return mShouldAnimateExpandPanel; + } + + @Override + protected void animateExpandPanel() { + super.animateExpandPanel(); + mAnimateExpandPanelCalled = true; + } + + @Override + protected void onAnimateExpandPanel() { + mOnAnimateExpandPanelCalled = true; + } + + @Override + protected void onCollapseAnimationEnd() { + mOnCollapseAnimationEndCalled = true; + } + + @Override + protected void onExpandAnimationEnd() { + mOnExpandAnimationEndCalled = true; + } + + @Override + protected void onScroll(int height) { + mOnScrollHeights.add(height); + } + + @Override + protected void onOpenScrollStart() { + mOnOpenScrollStartEnd = true; + } + + public void setShouldAllowClosingScroll(boolean shouldAllow) { + mShouldAllowClosingScroll = shouldAllow; + } + + @Override + protected boolean shouldAllowClosingScroll() { + return mShouldAllowClosingScroll; + } + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java index 3be962627f62..331326168ba4 100644 --- a/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java +++ b/packages/CarSystemUI/tests/src/com/android/systemui/window/OverlayViewControllerTest.java @@ -43,7 +43,7 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper @SmallTest public class OverlayViewControllerTest extends SysuiTestCase { - private MockOverlayViewController mOverlayViewController; + private TestOverlayViewController mOverlayViewController; private ViewGroup mBaseLayout; @Mock @@ -56,7 +56,7 @@ public class OverlayViewControllerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(/* testClass= */ this); - mOverlayViewController = new MockOverlayViewController(R.id.overlay_view_controller_stub, + mOverlayViewController = new TestOverlayViewController(R.id.overlay_view_controller_stub, mOverlayViewGlobalStateController); mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate( @@ -130,12 +130,12 @@ public class OverlayViewControllerTest extends SysuiTestCase { assertThat(mOverlayViewController.mHideInternalCalled).isFalse(); } - private static class MockOverlayViewController extends OverlayViewController { + private static class TestOverlayViewController extends OverlayViewController { boolean mOnFinishInflateCalled = false; boolean mShowInternalCalled = false; boolean mHideInternalCalled = false; - MockOverlayViewController(int stubId, + TestOverlayViewController(int stubId, OverlayViewGlobalStateController overlayViewGlobalStateController) { super(stubId, overlayViewGlobalStateController); } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java index 8aa0aec28fb8..a53bc9f966d2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java @@ -102,12 +102,11 @@ public class WifiEntryPreference extends Preference implements WifiEntry.WifiEnt // Turn off divider view.findViewById(R.id.two_target_divider).setVisibility(View.INVISIBLE); - // Enable the icon button when this Entry is a canManageSubscription entry. + // Enable the icon button when the help string in this WifiEntry is not null. final ImageButton imageButton = (ImageButton) view.findViewById(R.id.icon_button); final ImageView frictionImageView = (ImageView) view.findViewById( R.id.friction_icon); - if (mWifiEntry.canManageSubscription() && !mWifiEntry.isSaved() - && !mWifiEntry.isSubscription() + if (mWifiEntry.getHelpUriString() != null && mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_DISCONNECTED) { final Drawable drawablehelp = getDrawable(R.drawable.ic_help); drawablehelp.setTintList( diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java index a9f31ce12b42..46e699d3bed5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java @@ -64,7 +64,7 @@ public class WifiEntryPreferenceTest { private static final String MOCK_TITLE = "title"; private static final String MOCK_SUMMARY = "summary"; - + private static final String FAKE_URI_STRING = "fakeuri"; @Before public void setUp() { @@ -155,22 +155,23 @@ public class WifiEntryPreferenceTest { } @Test - public void canManageSubscription_shouldSetImageButtonVisible() { - when(mMockWifiEntry.canManageSubscription()).thenReturn(true); + public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() { + when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING); final WifiEntryPreference pref = new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector); final LayoutInflater inflater = LayoutInflater.from(mContext); final View view = inflater.inflate(pref.getLayoutResource(), new LinearLayout(mContext), false); final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view); + pref.onBindViewHolder(holder); assertThat(view.findViewById(R.id.icon_button).getVisibility()).isEqualTo(View.VISIBLE); } @Test - public void helpButton_whenCanManageSubscription_shouldSetCorrectContentDescription() { - when(mMockWifiEntry.canManageSubscription()).thenReturn(true); + public void helpButton_whenGetHelpUriStringNotNull_shouldSetCorrectContentDescription() { + when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING); final WifiEntryPreference pref = new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector); final LayoutInflater inflater = LayoutInflater.from(mContext); diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml index fc3bf941b27a..e5ac5f89cd25 100644 --- a/packages/SystemUI/res/layout/qs_media_panel.xml +++ b/packages/SystemUI/res/layout/qs_media_panel.xml @@ -23,7 +23,8 @@ android:layout_height="match_parent" android:orientation="vertical" android:gravity="center_horizontal|fill_vertical" - android:padding="16dp" + android:paddingTop="@dimen/qs_media_panel_outer_padding" + android:paddingBottom="@dimen/qs_media_panel_outer_padding" android:background="@drawable/qs_media_background" > @@ -42,7 +43,9 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="16dp" + android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" + android:paddingStart="@dimen/qs_media_panel_outer_padding" + android:paddingEnd="16dp" > <ImageView @@ -139,6 +142,7 @@ <!-- Seek Bar --> <SeekBar android:id="@+id/media_progress_bar" + style="@android:style/Widget.ProgressBar.Horizontal" android:clickable="true" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -154,6 +158,9 @@ android:id="@+id/notification_media_progress_time" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingStart="@dimen/qs_media_panel_outer_padding" + android:paddingEnd="@dimen/qs_media_panel_outer_padding" + android:layout_marginBottom="10dp" android:layout_gravity="center" > <!-- width is set to "match_parent" to avoid extra layout calls --> @@ -184,6 +191,8 @@ android:layoutDirection="ltr" android:layout_width="match_parent" android:layout_height="wrap_content" + android:paddingStart="@dimen/qs_media_panel_outer_padding" + android:paddingEnd="@dimen/qs_media_panel_outer_padding" android:gravity="center" > <ImageButton diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index bce5fac76cfc..344479f371d7 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1211,6 +1211,7 @@ <!-- Size of media cards in the QSPanel carousel --> <dimen name="qs_media_width">350dp</dimen> <dimen name="qs_media_padding">8dp</dimen> + <dimen name="qs_media_panel_outer_padding">16dp</dimen> <dimen name="qs_media_corner_radius">10dp</dimen> <dimen name="qs_media_album_size">72dp</dimen> <dimen name="qs_seamless_icon_size">20dp</dimen> diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java index 7eb5a8f2b06c..e99245fa438f 100644 --- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java @@ -34,6 +34,8 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; +import java.util.NoSuchElementException; + /** * Encapsulates all logic for secondary lockscreen state management. */ @@ -79,7 +81,9 @@ public class AdminSecondaryLockScreenController { private final IKeyguardCallback mCallback = new IKeyguardCallback.Stub() { @Override public void onDismiss() { - dismiss(UserHandle.getCallingUserId()); + mHandler.post(() -> { + dismiss(UserHandle.getCallingUserId()); + }); } @Override @@ -91,7 +95,9 @@ public class AdminSecondaryLockScreenController { if (surfacePackage != null) { mView.setChildSurfacePackage(surfacePackage); } else { - dismiss(KeyguardUpdateMonitor.getCurrentUser()); + mHandler.post(() -> { + dismiss(KeyguardUpdateMonitor.getCurrentUser()); + }); } } }; @@ -122,6 +128,7 @@ public class AdminSecondaryLockScreenController { // If the remote content is not readied within the timeout period, // move on without the secondary lockscreen. dismiss(userId); + Log.w(TAG, "Timed out waiting for secondary lockscreen content."); }, REMOTE_CONTENT_READY_TIMEOUT_MILLIS); } @@ -150,8 +157,12 @@ public class AdminSecondaryLockScreenController { * Displays the Admin security Surface view. */ public void show(Intent serviceIntent) { - mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); - mParent.addView(mView); + if (mClient == null) { + mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); + } + if (!mView.isAttachedToWindow()) { + mParent.addView(mView); + } } /** @@ -162,7 +173,11 @@ public class AdminSecondaryLockScreenController { mParent.removeView(mView); } if (mClient != null) { - mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0); + try { + mClient.asBinder().unlinkToDeath(mKeyguardClientDeathRecipient, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "IKeyguardClient death recipient already released"); + } mContext.unbindService(mConnection); mClient = null; } @@ -185,10 +200,12 @@ public class AdminSecondaryLockScreenController { private void dismiss(int userId) { mHandler.removeCallbacksAndMessages(null); - if (mView != null && mView.isAttachedToWindow() - && userId == KeyguardUpdateMonitor.getCurrentUser()) { + if (mView.isAttachedToWindow() && userId == KeyguardUpdateMonitor.getCurrentUser()) { hide(); - mKeyguardCallback.dismiss(true, userId); + if (mKeyguardCallback != null) { + mKeyguardCallback.dismiss(/* securityVerified= */ true, userId, + /* bypassSecondaryLockScreen= */true); + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java index d5a08dda9853..aa2fe3c7f8fc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java @@ -85,7 +85,8 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback { // the user proved presence via some other way to the trust agent. Log.i(TAG, "TrustAgent dismissed Keyguard."); } - dismiss(false /* authenticated */, userId); + dismiss(false /* authenticated */, userId, + /* bypassSecondaryLockScreen */ false); } else { mViewMediatorCallback.playTrustedSound(); } @@ -190,7 +191,7 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback { * @return True if the keyguard is done. */ public boolean dismiss(int targetUserId) { - return dismiss(false, targetUserId); + return dismiss(false, targetUserId, false); } public boolean handleBackKey() { @@ -206,8 +207,10 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback { } @Override - public boolean dismiss(boolean authenticated, int targetUserId) { - return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId); + public boolean dismiss(boolean authenticated, int targetUserId, + boolean bypassSecondaryLockScreen) { + return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId, + bypassSecondaryLockScreen); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java index 49dcfffb0d72..e38472745234 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java @@ -25,6 +25,15 @@ public interface KeyguardSecurityCallback { void dismiss(boolean securityVerified, int targetUserId); /** + * Dismiss the given security screen. + * @param securityVerified true if the user correctly entered credentials for the given screen. + * @param targetUserId a user that needs to be the foreground user at the dismissal completion. + * @param bypassSecondaryLockScreen true if the user can bypass the secondary lock screen, + * if any, during this dismissal. + */ + void dismiss(boolean securityVerified, int targetUserId, boolean bypassSecondaryLockScreen); + + /** * Manually report user activity to keep the device awake. */ void userActivity(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index ba8a1a945a77..1e1ce4e6d159 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -115,7 +115,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe // Used to notify the container when something interesting happens. public interface SecurityCallback { - public boolean dismiss(boolean authenticated, int targetUserId); + public boolean dismiss(boolean authenticated, int targetUserId, + boolean bypassSecondaryLockScreen); public void userActivity(); public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); @@ -504,9 +505,12 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe * @param authenticated true if the user entered the correct authentication * @param targetUserId a user that needs to be the foreground user at the finish (if called) * completion. + * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary + * secondary lock screen requirement, if any. * @return true if keyguard is done */ - boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) { + boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, + boolean bypassSecondaryLockScreen) { if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); boolean finish = false; boolean strongAuth = false; @@ -555,7 +559,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } } // Check for device admin specified additional security measures. - if (finish) { + if (finish && !bypassSecondaryLockScreen) { Intent secondaryLockscreenIntent = mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId); if (secondaryLockscreenIntent != null) { @@ -636,8 +640,15 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe mUpdateMonitor.cancelFaceAuth(); } + @Override public void dismiss(boolean authenticated, int targetId) { - mSecurityCallback.dismiss(authenticated, targetId); + dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false); + } + + @Override + public void dismiss(boolean authenticated, int targetId, + boolean bypassSecondaryLockScreen) { + mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen); } public boolean isVerifyUnlockOnly() { @@ -689,6 +700,9 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe @Override public void dismiss(boolean securityVerified, int targetUserId) { } @Override + public void dismiss(boolean authenticated, int targetId, + boolean bypassSecondaryLockScreen) { } + @Override public void onUserInput() { } @Override public void reset() {} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 9d885fd3c207..99e5eb66a00f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -719,13 +719,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } /** - * Tell the stack of bubbles to expand. - */ - public void expandStack() { - mBubbleData.setExpanded(true); - } - - /** * Tell the stack of bubbles to collapse. */ public void collapseStack() { @@ -753,12 +746,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed; } - @VisibleForTesting - void selectBubble(String key) { - Bubble bubble = mBubbleData.getBubbleWithKey(key); - mBubbleData.setSelectedBubble(bubble); - } - void promoteBubbleFromOverflow(Bubble bubble) { bubble.setInflateSynchronously(mInflateSynchronously); mBubbleData.promoteBubbleFromOverflow(bubble, mStackView, mBubbleIconFactory); @@ -778,13 +765,6 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } /** - * Tell the stack of bubbles to be dismissed, this will remove all of the bubbles in the stack. - */ - void dismissStack(@DismissReason int reason) { - mBubbleData.dismissAll(reason); - } - - /** * Directs a back gesture at the bubble stack. When opened, the current expanded bubble * is forwarded a back key down/up pair. */ diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index be9cd5f01c86..4c149ddd3939 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -28,6 +28,7 @@ import android.content.Context; import android.service.notification.NotificationListenerService; import android.util.Log; import android.util.Pair; +import android.view.View; import androidx.annotation.Nullable; @@ -751,6 +752,7 @@ public class BubbleData { } @VisibleForTesting(visibility = PRIVATE) + @Nullable Bubble getBubbleWithKey(String key) { for (int i = 0; i < mBubbles.size(); i++) { Bubble bubble = mBubbles.get(i); @@ -761,6 +763,17 @@ public class BubbleData { return null; } + @Nullable + Bubble getBubbleWithView(View view) { + for (int i = 0; i < mBubbles.size(); i++) { + Bubble bubble = mBubbles.get(i); + if (bubble.getIconView() != null && bubble.getIconView().equals(view)) { + return bubble; + } + } + return null; + } + @VisibleForTesting(visibility = PRIVATE) Bubble getOverflowBubbleWithKey(String key) { for (int i = 0; i < mOverflowBubbles.size(); i++) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 044feaa117c8..644e54fb82ae 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -30,6 +30,7 @@ import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.SuppressLint; import android.app.Notification; import android.content.Context; import android.content.res.Configuration; @@ -43,6 +44,7 @@ import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.Region; import android.os.Bundle; import android.os.Vibrator; import android.util.Log; @@ -83,6 +85,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.util.DismissCircleView; import com.android.systemui.util.FloatingContentCoordinator; +import com.android.systemui.util.RelativeTouchListener; import com.android.systemui.util.animation.PhysicsAnimator; import com.android.systemui.util.magnetictarget.MagnetizedObject; @@ -239,7 +242,6 @@ public class BubbleStackView extends FrameLayout { mExpandedAnimationController.dump(fd, pw, args); } - private BubbleTouchHandler mTouchHandler; private BubbleController.BubbleExpandListener mExpandListener; private SysUiState mSysUiState; @@ -296,7 +298,7 @@ public class BubbleStackView extends FrameLayout { @Override public void setValue(Object o, float v) { - onFlyoutDragged(v); + setFlyoutStateForDragLength(v); } }; @@ -337,13 +339,6 @@ public class BubbleStackView extends FrameLayout { private MagnetizedObject<?> mMagnetizedObject; /** - * The action to run when the magnetized object is released in the dismiss target. - * - * This will actually perform the dismissal of either the stack or an individual bubble. - */ - private Runnable mReleasedInDismissTargetAction; - - /** * The MagneticTarget instance for our circular dismiss view. This is added to the * MagnetizedObject instances for the stack and any dragged-out bubbles. */ @@ -377,7 +372,7 @@ public class BubbleStackView extends FrameLayout { public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { mExpandedAnimationController.dismissDraggedOutBubble( mExpandedAnimationController.getDraggedOutBubble(), - mReleasedInDismissTargetAction); + BubbleStackView.this::dismissMagnetizedObject); hideDismissTarget(); } }; @@ -410,7 +405,7 @@ public class BubbleStackView extends FrameLayout { mStackAnimationController.implodeStack( () -> { resetDesaturationAndDarken(); - mReleasedInDismissTargetAction.run(); + dismissMagnetizedObject(); } ); @@ -418,6 +413,197 @@ public class BubbleStackView extends FrameLayout { } }; + /** + * Click listener set on each bubble view. When collapsed, clicking a bubble expands the stack. + * When expanded, clicking a bubble either expands that bubble, or collapses the stack. + */ + private OnClickListener mBubbleClickListener = new OnClickListener() { + @Override + public void onClick(View view) { + final Bubble clickedBubble = mBubbleData.getBubbleWithView(view); + + // If the bubble has since left us, ignore the click. + if (clickedBubble == null) { + return; + } + + final boolean clickedBubbleIsCurrentlyExpandedBubble = + clickedBubble.getKey().equals(mExpandedBubble.getKey()); + + if (isExpanded() && !clickedBubbleIsCurrentlyExpandedBubble) { + if (clickedBubble != mBubbleData.getSelectedBubble()) { + // Select the clicked bubble. + mBubbleData.setSelectedBubble(clickedBubble); + } else { + // If the clicked bubble is the selected bubble (but not the expanded bubble), + // that means overflow was previously expanded. Set the selected bubble + // internally without going through BubbleData (which would ignore it since it's + // already selected). + setSelectedBubble(clickedBubble); + + } + } else { + // 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()) { + mBubbleData.setExpanded(!mBubbleData.isExpanded()); + } + } + } + }; + + /** + * Touch listener set on each bubble view. This enables dragging and dismissing the stack (when + * collapsed), or individual bubbles (when expanded). + */ + private RelativeTouchListener mBubbleTouchListener = new RelativeTouchListener() { + + @Override + public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) { + // If we're expanding or collapsing, consume but ignore all touch events. + if (mIsExpansionAnimating) { + return true; + } + + if (mBubbleData.isExpanded()) { + maybeShowManageEducation(false /* show */); + + // If we're expanded, tell the animation controller to prepare to drag this bubble, + // dispatching to the individual bubble magnet listener. + mExpandedAnimationController.prepareForBubbleDrag( + v /* bubble */, + mMagneticTarget, + mIndividualBubbleMagnetListener); + + // Save the magnetized individual bubble so we can dispatch touch events to it. + mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut(); + } else { + // If we're collapsed, prepare to drag the stack. Cancel active animations, set the + // animation controller, and hide the flyout. + mStackAnimationController.cancelStackPositionAnimations(); + mBubbleContainer.setActiveController(mStackAnimationController); + hideFlyoutImmediate(); + + // Also, save the magnetized stack so we can dispatch touch events to it. + mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget); + mMagnetizedObject.setMagnetListener(mStackMagnetListener); + } + + passEventToMagnetizedObject(ev); + + // Bubbles are always interested in all touch events! + return true; + } + + @Override + public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY, float dx, float dy) { + // If we're expanding or collapsing, ignore all touch events. + if (mIsExpansionAnimating) { + return; + } + + // Show the dismiss target, if we haven't already. + springInDismissTargetMaybe(); + + // First, see if the magnetized object consumes the event - if so, we shouldn't move the + // bubble since it's stuck to the target. + if (!passEventToMagnetizedObject(ev)) { + if (mBubbleData.isExpanded()) { + mExpandedAnimationController.dragBubbleOut( + v, viewInitialX + dx, viewInitialY + dy); + } else { + hideStackUserEducation(false /* fromExpansion */); + mStackAnimationController.moveStackFromTouch( + viewInitialX + dx, viewInitialY + dy); + } + } + } + + @Override + public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY, float dx, float dy, float velX, float velY) { + // If we're expanding or collapsing, ignore all touch events. + if (mIsExpansionAnimating) { + return; + } + + // First, see if the magnetized object consumes the event - if so, the bubble was + // released in the target or flung out of it, and we should ignore the event. + if (!passEventToMagnetizedObject(ev)) { + if (mBubbleData.isExpanded()) { + mExpandedAnimationController.snapBubbleBack(v, velX, velY); + } else { + // Fling the stack to the edge, and save whether or not it's going to end up on + // the left side of the screen. + mStackOnLeftOrWillBe = + mStackAnimationController.flingStackThenSpringToEdge( + viewInitialX + dx, velX, velY) <= 0; + + updateBubbleZOrdersAndDotPosition(true /* animate */); + + logBubbleEvent(null /* no bubble associated with bubble stack move */, + SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED); + } + + hideDismissTarget(); + } + } + }; + + /** Click listener set on the flyout, which expands the stack when the flyout is tapped. */ + private OnClickListener mFlyoutClickListener = new OnClickListener() { + @Override + public void onClick(View view) { + if (maybeShowStackUserEducation()) { + // If we're showing user education, don't open the bubble show the education first + mBubbleToExpandAfterFlyoutCollapse = null; + } else { + mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble(); + } + + mFlyout.removeCallbacks(mHideFlyout); + mHideFlyout.run(); + } + }; + + /** Touch listener for the flyout. This enables the drag-to-dismiss gesture on the flyout. */ + private RelativeTouchListener mFlyoutTouchListener = new RelativeTouchListener() { + + @Override + public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) { + mFlyout.removeCallbacks(mHideFlyout); + return true; + } + + @Override + public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY, float dx, float dy) { + setFlyoutStateForDragLength(dx); + } + + @Override + public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY, float dx, float dy, float velX, float velY) { + final boolean onLeft = mStackAnimationController.isStackOnLeftSide(); + final boolean metRequiredVelocity = + onLeft ? velX < -FLYOUT_DISMISS_VELOCITY : velX > FLYOUT_DISMISS_VELOCITY; + final boolean metRequiredDeltaX = + onLeft + ? dx < -mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS + : dx > mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS; + final boolean isCancelFling = onLeft ? velX > 0 : velX < 0; + final boolean shouldDismiss = metRequiredVelocity + || (metRequiredDeltaX && !isCancelFling); + + mFlyout.removeCallbacks(mHideFlyout); + animateFlyoutCollapsed(shouldDismiss, velX); + + maybeShowStackUserEducation(); + } + }; + private ViewGroup mDismissTargetContainer; private PhysicsAnimator<View> mDismissTargetAnimator; private PhysicsAnimator.SpringConfig mDismissTargetSpring = new PhysicsAnimator.SpringConfig( @@ -436,6 +622,7 @@ public class BubbleStackView extends FrameLayout { private BubbleManageEducationView mManageEducationView; private boolean mAnimatingManageEducationAway; + @SuppressLint("ClickableViewAccessibility") public BubbleStackView(Context context, BubbleData data, @Nullable SurfaceSynchronizer synchronizer, FloatingContentCoordinator floatingContentCoordinator, @@ -444,8 +631,6 @@ public class BubbleStackView extends FrameLayout { mBubbleData = data; mInflater = LayoutInflater.from(context); - mTouchHandler = new BubbleTouchHandler(this, data, context); - setOnTouchListener(mTouchHandler); mSysUiState = sysUiState; @@ -641,6 +826,18 @@ public class BubbleStackView extends FrameLayout { mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix)); mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint); }); + + // If the stack itself is touched, it means none of its touchable views (bubbles, flyouts, + // ActivityViews, etc.) were touched. Collapse the stack if it's expanded. + setOnTouchListener((view, ev) -> { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + if (mBubbleData.isExpanded()) { + mBubbleData.setExpanded(false); + } + } + + return false; + }); } private void setUpUserEducation() { @@ -690,6 +887,7 @@ public class BubbleStackView extends FrameLayout { } } + @SuppressLint("ClickableViewAccessibility") private void setUpFlyout() { if (mFlyout != null) { removeView(mFlyout); @@ -699,6 +897,8 @@ public class BubbleStackView extends FrameLayout { mFlyout.animate() .setDuration(FLYOUT_ALPHA_ANIMATION_DURATION) .setInterpolator(new AccelerateDecelerateInterpolator()); + mFlyout.setOnClickListener(mFlyoutClickListener); + mFlyout.setOnTouchListener(mFlyoutTouchListener); addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); } @@ -718,6 +918,7 @@ public class BubbleStackView extends FrameLayout { mBubbleContainer.addView(mBubbleOverflow.getBtn(), overflowBtnIndex, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); + mBubbleOverflow.getBtn().setOnClickListener(v -> setSelectedBubble(mBubbleOverflow)); } /** * Handle theme changes. @@ -920,6 +1121,7 @@ public class BubbleStackView extends FrameLayout { } // via BubbleData.Listener + @SuppressLint("ClickableViewAccessibility") void addBubble(Bubble bubble) { if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "addBubble: " + bubble); @@ -944,6 +1146,9 @@ public class BubbleStackView extends FrameLayout { bubble.getIconView().setDotPositionOnLeft( !mStackOnLeftOrWillBe /* onLeft */, false /* animate */); + bubble.getIconView().setOnClickListener(mBubbleClickListener); + bubble.getIconView().setOnTouchListener(mBubbleTouchListener); + mBubbleContainer.addView(bubble.getIconView(), 0, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); ViewClippingUtil.setClippingDeactivated(bubble.getIconView(), true, mClippingParameters); @@ -1009,10 +1214,6 @@ public class BubbleStackView extends FrameLayout { updatePointerPosition(); } - void showOverflow() { - setSelectedBubble(mBubbleOverflow); - } - /** * Changes the currently selected bubble. If the stack is already expanded, the newly selected * bubble will be shown immediately. This does not change the expanded state or change the @@ -1177,14 +1378,6 @@ public class BubbleStackView extends FrameLayout { } } - /* - * Sets the action to run to dismiss the currently dragging object (either the stack or an - * individual bubble). - */ - public void setReleasedInDismissTargetAction(Runnable action) { - mReleasedInDismissTargetAction = action; - } - /** * Dismiss the stack of bubbles. * @@ -1201,54 +1394,6 @@ public class BubbleStackView extends FrameLayout { } /** - * @return the view the touch event is on - */ - @Nullable - public View getTargetView(MotionEvent event) { - float x = event.getRawX(); - float y = event.getRawY(); - if (mIsExpanded) { - if (isIntersecting(mBubbleContainer, x, y)) { - if (BubbleExperimentConfig.allowBubbleOverflow(mContext) - && isIntersecting(mBubbleOverflow.getBtn(), x, y)) { - return mBubbleOverflow.getBtn(); - } - // Could be tapping or dragging a bubble while expanded - for (int i = 0; i < getBubbleCount(); i++) { - BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i); - if (isIntersecting(view, x, y)) { - return view; - } - } - } - BubbleExpandedView bev = (BubbleExpandedView) mExpandedViewContainer.getChildAt(0); - if (bev.intersectingTouchableContent((int) x, (int) y)) { - return bev; - } - // Outside of the parts we care about. - return null; - } else if (mFlyout.getVisibility() == VISIBLE && isIntersecting(mFlyout, x, y)) { - return mFlyout; - } else if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) { - View bubbleChild = mBubbleContainer.getChildAt(0); - if (isIntersecting(bubbleChild, x, y)) { - return this; - } else if (isIntersecting(mUserEducationView, x, y)) { - return mUserEducationView; - } else { - return null; - } - } - - // If it wasn't an individual bubble in the expanded state, or the flyout, it's the stack. - return this; - } - - View getFlyoutView() { - return mFlyout; - } - - /** * @deprecated use {@link #setExpanded(boolean)} and * {@link BubbleData#setSelectedBubble(Bubble)} */ @@ -1385,124 +1530,70 @@ public class BubbleStackView extends FrameLayout { } } - /** Called when the collapsed stack is tapped on. */ - void onStackTapped() { - if (!maybeShowStackUserEducation()) { - mBubbleData.setExpanded(true); - } - } - - /** Called when a drag operation on an individual bubble has started. */ - public void onBubbleDragStart(View bubble) { - if (DEBUG_BUBBLE_STACK_VIEW) { - Log.d(TAG, "onBubbleDragStart: bubble=" + ((BadgedImageView) bubble).getKey()); - } - - if (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView())) { - return; - } - - mExpandedAnimationController.prepareForBubbleDrag(bubble, mMagneticTarget); - - // We're dragging an individual bubble, so set the magnetized object to the magnetized - // bubble. - mMagnetizedObject = mExpandedAnimationController.getMagnetizedBubbleDraggingOut(); - mMagnetizedObject.setMagnetListener(mIndividualBubbleMagnetListener); - - maybeShowManageEducation(false); - } - - /** Called with the coordinates to which an individual bubble has been dragged. */ - public void onBubbleDragged(View bubble, float x, float y) { - if (!mIsExpanded || mIsExpansionAnimating - || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) { - return; - } - - mExpandedAnimationController.dragBubbleOut(bubble, x, y); - springInDismissTarget(); - } - - /** Called when a drag operation on an individual bubble has finished. */ - public void onBubbleDragFinish( - View bubble, float x, float y, float velX, float velY) { - if (DEBUG_BUBBLE_STACK_VIEW) { - Log.d(TAG, "onBubbleDragFinish: bubble=" + bubble); - } - - if (!mIsExpanded || mIsExpansionAnimating - || (mBubbleOverflow != null && bubble.equals(mBubbleOverflow.getIconView()))) { - return; - } - - mExpandedAnimationController.snapBubbleBack(bubble, velX, velY); - hideDismissTarget(); - } - - /** Expands the clicked bubble. */ - public void expandBubble(Bubble bubble) { - if (bubble != null && bubble.equals(mBubbleData.getSelectedBubble())) { - // If the bubble we're supposed to expand is the selected bubble, that means the - // overflow bubble is currently expanded. Don't tell BubbleData to set this bubble as - // selected, since it already is. Just call the stack's setSelectedBubble to expand it. - setSelectedBubble(bubble); - } else { - mBubbleData.setSelectedBubble(bubble); - } - } - - void onDragStart() { - if (DEBUG_BUBBLE_STACK_VIEW) { - Log.d(TAG, "onDragStart()"); - } - if (mIsExpanded || mIsExpansionAnimating) { - if (DEBUG_BUBBLE_STACK_VIEW) { - Log.d(TAG, "mIsExpanded or mIsExpansionAnimating"); - } - return; - } - mStackAnimationController.cancelStackPositionAnimations(); - mBubbleContainer.setActiveController(mStackAnimationController); - hideFlyoutImmediate(); + /** + * This method is called by {@link android.app.ActivityView} because the BubbleStackView has a + * higher Z-index than the ActivityView (so that dragged-out bubbles are visible over the AV). + * ActivityView is asking BubbleStackView to subtract the stack's bounds from the provided + * touchable region, so that the ActivityView doesn't consume events meant for the stack. Due to + * the special nature of ActivityView, it does not respect the standard + * {@link #dispatchTouchEvent} and {@link #onInterceptTouchEvent} methods typically used for + * this purpose. + * + * BubbleStackView is MATCH_PARENT, so that bubbles can be positioned via their translation + * properties for performance reasons. This means that the default implementation of this method + * subtracts the entirety of the screen from the ActivityView's touchable region, resulting in + * it not receiving any touch events. This was previously addressed by returning false in the + * stack's {@link View#canReceivePointerEvents()} method, but this precluded the use of any + * touch handlers in the stack or its child views. + * + * To support touch handlers, we're overriding this method to leave the ActivityView's touchable + * region alone. The only touchable part of the stack that can ever overlap the AV is a + * dragged-out bubble that is animating back into the row of bubbles. It's not worth continually + * updating the touchable region to allow users to grab a bubble while it completes its ~50ms + * animation back to the bubble row. + * + * NOTE: Any future additions to the stack that obscure the ActivityView region will need their + * bounds subtracted here in order to receive touch events. + */ + @Override + public void subtractObscuredTouchableRegion(Region touchableRegion, View view) { - // Since we're dragging the stack, set the magnetized object to the magnetized stack. - mMagnetizedObject = mStackAnimationController.getMagnetizedStack(mMagneticTarget); - mMagnetizedObject.setMagnetListener(mStackMagnetListener); } - void onDragged(float x, float y) { - if (mIsExpanded || mIsExpansionAnimating) { - return; - } - - hideStackUserEducation(false /* fromExpansion */); - springInDismissTarget(); - mStackAnimationController.moveStackFromTouch(x, y); + /** + * If you're here because you're not receiving touch events on a view that is a descendant of + * BubbleStackView, and you think BSV is intercepting them - it's not! You need to subtract the + * bounds of the view in question in {@link #subtractObscuredTouchableRegion}. The ActivityView + * consumes all touch events within its bounds, even for views like the BubbleStackView that are + * above it. It ignores typical view touch handling methods like this one and + * dispatchTouchEvent. + */ + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return super.onInterceptTouchEvent(ev); } - void onDragFinish(float x, float y, float velX, float velY) { - if (DEBUG_BUBBLE_STACK_VIEW) { - Log.d(TAG, "onDragFinish"); - } + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + boolean dispatched = super.dispatchTouchEvent(ev); - if (mIsExpanded || mIsExpansionAnimating) { - return; + // If a new bubble arrives while the collapsed stack is being dragged, it will be positioned + // at the front of the stack (under the touch position). Subsequent ACTION_MOVE events will + // then be passed to the new bubble, which will not consume them since it hasn't received an + // ACTION_DOWN yet. Work around this by passing MotionEvents directly to the touch handler + // until the current gesture ends with an ACTION_UP event. + if (!dispatched && !mIsExpanded && mIsGestureInProgress) { + dispatched = mBubbleTouchListener.onTouch(this /* view */, ev); } - final float newStackX = mStackAnimationController.flingStackThenSpringToEdge(x, velX, velY); - logBubbleEvent(null /* no bubble associated with bubble stack move */, - SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED); - - mStackOnLeftOrWillBe = newStackX <= 0; - updateBubbleZOrdersAndDotPosition(true /* animate */); - hideDismissTarget(); - } + mIsGestureInProgress = + ev.getAction() != MotionEvent.ACTION_UP + && ev.getAction() != MotionEvent.ACTION_CANCEL; - void onFlyoutDragStart() { - mFlyout.removeCallbacks(mHideFlyout); + return dispatched; } - void onFlyoutDragged(float deltaX) { + void setFlyoutStateForDragLength(float deltaX) { // This shouldn't happen, but if it does, just wait until the flyout lays out. This method // is continually called. if (mFlyout.getWidth() <= 0) { @@ -1538,59 +1629,27 @@ public class BubbleStackView extends FrameLayout { mFlyout.setTranslationX(mFlyout.getRestingTranslationX() + overscrollTranslation); } - void onFlyoutTapped() { - if (maybeShowStackUserEducation()) { - // If we're showing user education, don't open the bubble show the education first - mBubbleToExpandAfterFlyoutCollapse = null; - } else { - mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble(); - } - - mFlyout.removeCallbacks(mHideFlyout); - mHideFlyout.run(); - } - - /** - * Called when the flyout drag has finished, and returns true if the gesture successfully - * dismissed the flyout. - */ - void onFlyoutDragFinished(float deltaX, float velX) { - final boolean onLeft = mStackAnimationController.isStackOnLeftSide(); - final boolean metRequiredVelocity = - onLeft ? velX < -FLYOUT_DISMISS_VELOCITY : velX > FLYOUT_DISMISS_VELOCITY; - final boolean metRequiredDeltaX = - onLeft - ? deltaX < -mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS - : deltaX > mFlyout.getWidth() * FLYOUT_DRAG_PERCENT_DISMISS; - final boolean isCancelFling = onLeft ? velX > 0 : velX < 0; - final boolean shouldDismiss = metRequiredVelocity || (metRequiredDeltaX && !isCancelFling); - - mFlyout.removeCallbacks(mHideFlyout); - animateFlyoutCollapsed(shouldDismiss, velX); - - maybeShowStackUserEducation(); + /** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */ + private boolean passEventToMagnetizedObject(MotionEvent event) { + return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event); } /** - * Called when the first touch event of a gesture (stack drag, bubble drag, flyout drag, etc.) - * is received. + * Dismisses the magnetized object - either an individual bubble, if we're expanded, or the + * stack, if we're collapsed. */ - void onGestureStart() { - mIsGestureInProgress = true; - } - - /** Called when a gesture is completed or cancelled. */ - void onGestureFinished() { - mIsGestureInProgress = false; - + private void dismissMagnetizedObject() { if (mIsExpanded) { - mExpandedAnimationController.onGestureFinished(); - } - } + final View draggedOutBubbleView = (View) mMagnetizedObject.getUnderlyingObject(); + final Bubble draggedOutBubble = mBubbleData.getBubbleWithView(draggedOutBubbleView); - /** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */ - boolean passEventToMagnetizedObject(MotionEvent event) { - return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event); + if (mBubbleData.hasBubbleWithKey(draggedOutBubble.getKey())) { + mBubbleData.notificationEntryRemoved( + draggedOutBubble.getEntry(), BubbleController.DISMISS_USER_GESTURE); + } + } else { + mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); + } } /** Prepares and starts the desaturate/darken animation on the bubble stack. */ @@ -1624,7 +1683,7 @@ public class BubbleStackView extends FrameLayout { } /** Animates in the dismiss target. */ - private void springInDismissTarget() { + private void springInDismissTargetMaybe() { if (mShowingDismiss) { return; } @@ -1827,13 +1886,6 @@ public class BubbleStackView extends FrameLayout { return 0; } - private boolean isIntersecting(View view, float x, float y) { - mTempLoc = view.getLocationOnScreen(); - mTempRect.set(mTempLoc[0], mTempLoc[1], mTempLoc[0] + view.getWidth(), - mTempLoc[1] + view.getHeight()); - return mTempRect.contains(x, y); - } - private void requestUpdate() { if (mViewUpdatedRequested || mIsExpansionAnimating) { return; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java deleted file mode 100644 index 132c45fab3d2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2012 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.PointF; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.View; -import android.view.ViewConfiguration; - -import com.android.systemui.Dependency; - -/** - * Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing, - * dismissing, and flings. - */ -class BubbleTouchHandler implements View.OnTouchListener { - - private final PointF mTouchDown = new PointF(); - private final PointF mViewPositionOnTouchDown = new PointF(); - private final BubbleStackView mStack; - private final BubbleData mBubbleData; - - private BubbleController mController = Dependency.get(BubbleController.class); - - private boolean mMovedEnough; - private int mTouchSlopSquared; - private VelocityTracker mVelocityTracker; - - /** View that was initially touched, when we received the first ACTION_DOWN event. */ - private View mTouchedView; - - BubbleTouchHandler(BubbleStackView stackView, - BubbleData bubbleData, Context context) { - final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); - mTouchSlopSquared = touchSlop * touchSlop; - mBubbleData = bubbleData; - mStack = stackView; - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - final int action = event.getActionMasked(); - - // If we aren't currently in the process of touching a view, figure out what we're touching. - // It'll be the stack, an individual bubble, or nothing. - if (mTouchedView == null) { - mTouchedView = mStack.getTargetView(event); - } - - // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching - // anything, collapse the stack. - if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) { - mBubbleData.setExpanded(false); - mStack.hideStackUserEducation(false /* fromExpansion */); - resetForNextGesture(); - return false; - } - - if (!(mTouchedView instanceof BadgedImageView) - && !(mTouchedView instanceof BubbleStackView) - && !(mTouchedView instanceof BubbleFlyoutView)) { - - // Not touching anything touchable, but we shouldn't collapse (e.g. touching edge - // of expanded view). - mStack.maybeShowManageEducation(false); - resetForNextGesture(); - return false; - } - - final boolean isStack = mStack.equals(mTouchedView); - final boolean isFlyout = mStack.getFlyoutView().equals(mTouchedView); - final float rawX = event.getRawX(); - final float rawY = event.getRawY(); - - // The coordinates of the touch event, in terms of the touched view's position. - final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x; - final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y; - switch (action) { - case MotionEvent.ACTION_DOWN: - trackMovement(event); - - mTouchDown.set(rawX, rawY); - mStack.onGestureStart(); - - if (isStack) { - mViewPositionOnTouchDown.set(mStack.getStackPosition()); - - // Dismiss the entire stack if it's released in the dismiss target. - mStack.setReleasedInDismissTargetAction( - () -> mController.dismissStack(BubbleController.DISMISS_USER_GESTURE)); - mStack.onDragStart(); - mStack.passEventToMagnetizedObject(event); - } else if (isFlyout) { - mStack.onFlyoutDragStart(); - } else { - mViewPositionOnTouchDown.set( - mTouchedView.getTranslationX(), mTouchedView.getTranslationY()); - - // Dismiss only the dragged-out bubble if it's released in the target. - final String individualBubbleKey = ((BadgedImageView) mTouchedView).getKey(); - mStack.setReleasedInDismissTargetAction(() -> { - final Bubble bubble = - mBubbleData.getBubbleWithKey(individualBubbleKey); - // bubble can be null if the user is in the middle of - // dismissing the bubble, but the app also sent a cancel - if (bubble != null) { - mController.removeBubble(bubble.getEntry(), - BubbleController.DISMISS_USER_GESTURE); - } - }); - - mStack.onBubbleDragStart(mTouchedView); - mStack.passEventToMagnetizedObject(event); - } - - break; - case MotionEvent.ACTION_MOVE: - trackMovement(event); - final float deltaX = rawX - mTouchDown.x; - final float deltaY = rawY - mTouchDown.y; - - if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) { - mMovedEnough = true; - } - - if (mMovedEnough) { - if (isFlyout) { - mStack.onFlyoutDragged(deltaX); - } else if (!mStack.passEventToMagnetizedObject(event)) { - // If the magnetic target doesn't consume the event, drag the stack or - // bubble. - if (isStack) { - mStack.onDragged(viewX, viewY); - } else { - mStack.onBubbleDragged(mTouchedView, viewX, viewY); - } - } - } - break; - - case MotionEvent.ACTION_CANCEL: - resetForNextGesture(); - break; - - case MotionEvent.ACTION_UP: - trackMovement(event); - mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000); - final float velX = mVelocityTracker.getXVelocity(); - final float velY = mVelocityTracker.getYVelocity(); - - if (isFlyout && mMovedEnough) { - mStack.onFlyoutDragFinished(rawX - mTouchDown.x /* deltaX */, velX); - } else if (isFlyout) { - if (!mBubbleData.isExpanded() && !mMovedEnough) { - mStack.onFlyoutTapped(); - } - } else if (mMovedEnough) { - if (!mStack.passEventToMagnetizedObject(event)) { - // If the magnetic target didn't consume the event, tell the stack to finish - // the drag. - if (isStack) { - mStack.onDragFinish(viewX, viewY, velX, velY); - } else { - mStack.onBubbleDragFinish(mTouchedView, viewX, viewY, velX, velY); - } - } - } else if (mTouchedView == mStack.getExpandedBubbleView()) { - mBubbleData.setExpanded(false); - } else if (isStack) { - mStack.onStackTapped(); - } else { - final String key = ((BadgedImageView) mTouchedView).getKey(); - if (key == BubbleOverflow.KEY) { - mStack.showOverflow(); - } else { - mStack.expandBubble(mBubbleData.getBubbleWithKey(key)); - } - } - resetForNextGesture(); - break; - } - - return true; - } - - /** Clears all touch-related state. */ - private void resetForNextGesture() { - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - - mTouchedView = null; - mMovedEnough = false; - - mStack.onGestureFinished(); - } - - private void trackMovement(MotionEvent event) { - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(event); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index a0b49384d49f..d974adc34ee0 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -252,8 +252,14 @@ public class ExpandedAnimationController mSpringToTouchOnNextMotionEvent = true; } - /** Prepares the given bubble to be dragged out. */ - public void prepareForBubbleDrag(View bubble, MagnetizedObject.MagneticTarget target) { + /** + * Prepares the given bubble view to be dragged out, using the provided magnetic target and + * listener. + */ + public void prepareForBubbleDrag( + View bubble, + MagnetizedObject.MagneticTarget target, + MagnetizedObject.MagnetListener listener) { mLayout.cancelAnimationsOnView(bubble); bubble.setTranslationZ(Short.MAX_VALUE); @@ -277,6 +283,7 @@ public class ExpandedAnimationController } }; mMagnetizedBubbleDraggingOut.addTarget(target); + mMagnetizedBubbleDraggingOut.setMagnetListener(listener); mMagnetizedBubbleDraggingOut.setHapticsEnabled(true); mMagnetizedBubbleDraggingOut.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java index c292769f1066..b1bbafc1ed8f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java @@ -1117,9 +1117,4 @@ public class PhysicsAnimationLayout extends FrameLayout { mAssociatedController = controller; } } - - @Override - protected boolean canReceivePointerEvents() { - return false; - } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt index d33cd94004fd..8fd840e6cf58 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt @@ -56,13 +56,12 @@ data class RenderInfo( enabled: Boolean, offset: Int = 0 ): RenderInfo { - val (fg, bg) = deviceColorMap.getValue(deviceType) - - val iconKey = if (offset > 0) { + val key = if (offset > 0) { deviceType * BUCKET_SIZE + offset } else deviceType - val iconState = deviceIconMap.getValue(iconKey) + val (fg, bg) = deviceColorMap.getValue(key) + val iconState = deviceIconMap.getValue(key) val resourceId = iconState[enabled] var icon: Drawable? if (resourceId == APP_ICON_ID) { diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt index aa5ebaa22f2d..b7658a9f178d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt @@ -46,19 +46,6 @@ class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> { /** Updates seek bar views when the data model changes. */ @UiThread override fun onChanged(data: SeekBarViewModel.Progress) { - if (data.enabled && seekBarView.visibility == View.GONE) { - seekBarView.visibility = View.VISIBLE - elapsedTimeView.visibility = View.VISIBLE - totalTimeView.visibility = View.VISIBLE - } else if (!data.enabled && seekBarView.visibility == View.VISIBLE) { - seekBarView.visibility = View.GONE - elapsedTimeView.visibility = View.GONE - totalTimeView.visibility = View.GONE - return - } - - // TODO: update the style of the disabled progress bar - seekBarView.setEnabled(data.seekAvailable) data.color?.let { var tintList = ColorStateList.valueOf(it) @@ -71,6 +58,17 @@ class SeekBarObserver(view: View) : Observer<SeekBarViewModel.Progress> { totalTimeView.setTextColor(it) } + if (!data.enabled) { + seekBarView.setEnabled(false) + seekBarView.getThumb().setAlpha(0) + elapsedTimeView.setText("") + totalTimeView.setText("") + return + } + + seekBarView.getThumb().setAlpha(if (data.seekAvailable) 255 else 0) + seekBarView.setEnabled(data.seekAvailable) + data.elapsedTime?.let { seekBarView.setProgress(it) elapsedTimeView.setText(DateUtils.formatElapsedTime( diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index b10dd93fa695..d2994aebeb33 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -103,6 +103,7 @@ public class PipTaskOrganizer extends TaskOrganizer { @Override public void onPipAnimationEnd(SurfaceControl.Transaction tx, PipAnimationController.PipTransitionAnimator animator) { + finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection()); mMainHandler.post(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); @@ -110,7 +111,6 @@ public class PipTaskOrganizer extends TaskOrganizer { animator.getTransitionDirection()); } }); - finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index a192afceddb9..a8a5d896537f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -16,12 +16,10 @@ package com.android.systemui.pip.phone; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager.StackInfo; import android.app.IActivityTaskManager; import android.content.Context; import android.graphics.Rect; @@ -169,15 +167,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ void synchronizePinnedStackBounds() { cancelAnimations(); - try { - StackInfo stackInfo = mActivityTaskManager.getStackInfo( - WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); - if (stackInfo != null) { - mBounds.set(stackInfo.bounds); - } - } catch (RemoteException e) { - Log.w(TAG, "Failed to get pinned stack bounds"); - } + mBounds.set(mPipTaskOrganizer.getLastReportedBounds()); } /** 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 52c8960d1ccf..99a01d3f6a7f 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -289,7 +289,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMediaSessionManager = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE); - mPipTaskOrganizer.registerPipTransitionCallback(this); try { WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); @@ -334,6 +333,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio * Shows the picture-in-picture menu if an activity is in picture-in-picture mode. */ public void showPictureInPictureMenu() { + if (DEBUG) Log.d(TAG, "showPictureInPictureMenu(), current state=" + getStateDescription()); + if (getState() == STATE_PIP) { resizePinnedStack(STATE_PIP_MENU); } @@ -343,10 +344,18 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio * Closes PIP (PIPed activity and PIP system UI). */ public void closePip() { + if (DEBUG) Log.d(TAG, "closePip(), current state=" + getStateDescription()); + closePipInternal(true); } private void closePipInternal(boolean removePipStack) { + if (DEBUG) { + Log.d(TAG, + "closePipInternal() removePipStack=" + removePipStack + ", current state=" + + getStateDescription()); + } + mState = STATE_NO_PIP; mPipTaskId = TASK_ID_NO_PIP; mPipMediaController = null; @@ -371,6 +380,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio * Moves the PIPed activity to the fullscreen and closes PIP system UI. */ void movePipToFullscreen() { + if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription()); + mPipTaskId = TASK_ID_NO_PIP; for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onMoveToFullscreen(); @@ -386,6 +397,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio public void suspendPipResizing(int reason) { if (DEBUG) Log.d(TAG, "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2)); + mSuspendPipResizingReason |= reason; } @@ -408,7 +420,11 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio * @param state In Pip state also used to determine the new size for the Pip. */ void resizePinnedStack(int state) { - if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state, new Exception()); + if (DEBUG) { + Log.d(TAG, "resizePinnedStack() state=" + stateToName(state) + ", current state=" + + getStateDescription(), new Exception()); + } + boolean wasStateNoPip = (mState == STATE_NO_PIP); for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onPipResizeAboutToStart(); @@ -418,7 +434,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio if (DEBUG) Log.d(TAG, "resizePinnedStack() deferring" + " mSuspendPipResizingReason=" + mSuspendPipResizingReason + " mResumeResizePinnedStackRunnableState=" - + mResumeResizePinnedStackRunnableState); + + stateToName(mResumeResizePinnedStackRunnableState)); return; } mState = state; @@ -458,7 +474,8 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio * stack to the centered PIP bound {@link R.config_centeredPictureInPictureBounds}. */ private void showPipMenu() { - if (DEBUG) Log.d(TAG, "showPipMenu()"); + if (DEBUG) Log.d(TAG, "showPipMenu(), current state=" + getStateDescription()); + mState = STATE_PIP_MENU; for (int i = mListeners.size() - 1; i >= 0; --i) { mListeners.get(i).onShowPipMenu(); @@ -712,6 +729,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio private void onPipTransitionFinishedOrCanceled() { if (DEBUG) Log.d(TAG, "onPipTransitionFinishedOrCanceled()"); + if (getState() == STATE_PIP_MENU) { showPipMenu(); } @@ -753,4 +771,28 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio WindowManagerWrapper.getInstance().setPipVisibility(visible); }); } + + private String getStateDescription() { + if (mSuspendPipResizingReason == 0) { + return stateToName(mState); + } + return stateToName(mResumeResizePinnedStackRunnableState) + " (while " + stateToName(mState) + + " is suspended)"; + } + + private static String stateToName(int state) { + switch (state) { + case STATE_NO_PIP: + return "NO_PIP"; + + case STATE_PIP: + return "PIP"; + + case STATE_PIP_MENU: + return "PIP_MENU"; + + default: + return "UNKNOWN(" + state + ")"; + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java index f43f8e795fe6..c7e77ccfa488 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java @@ -22,6 +22,7 @@ import android.app.Activity; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.os.Bundle; +import android.util.Log; import com.android.systemui.R; import com.android.systemui.pip.tv.dagger.TvPipComponent; @@ -34,6 +35,7 @@ import javax.inject.Inject; * Activity to show the PIP menu to control PIP. */ public class PipMenuActivity extends Activity implements PipManager.Listener { + private static final boolean DEBUG = false; private static final String TAG = "PipMenuActivity"; static final String EXTRA_CUSTOM_ACTIONS = "custom_actions"; @@ -47,7 +49,6 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { private boolean mRestorePipSizeWhenClose; private PipControlsViewController mPipControlsViewController; - @Inject public PipMenuActivity(TvPipComponent.Builder pipComponentBuilder, PipManager pipManager) { super(); @@ -57,6 +58,8 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override protected void onCreate(Bundle bundle) { + if (DEBUG) Log.d(TAG, "onCreate()"); + super.onCreate(bundle); if (!mPipManager.isPipShown()) { finish(); @@ -81,13 +84,18 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override protected void onNewIntent(Intent intent) { + if (DEBUG) Log.d(TAG, "onNewIntent(), intent=" + intent); super.onNewIntent(intent); onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS)); } private void restorePipAndFinish() { + if (DEBUG) Log.d(TAG, "restorePipAndFinish()"); + if (mRestorePipSizeWhenClose) { + if (DEBUG) Log.d(TAG, " > restoring to the default position"); + // When PIP menu activity is closed, restore to the default position. mPipManager.resizePinnedStack(PipManager.STATE_PIP); } @@ -96,12 +104,16 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override public void onResume() { + if (DEBUG) Log.d(TAG, "onResume()"); + super.onResume(); mFadeInAnimation.start(); } @Override public void onPause() { + if (DEBUG) Log.d(TAG, "onPause()"); + super.onPause(); mFadeOutAnimation.start(); restorePipAndFinish(); @@ -109,6 +121,8 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override protected void onDestroy() { + if (DEBUG) Log.d(TAG, "onDestroy()"); + super.onDestroy(); mPipManager.removeListener(this); mPipManager.resumePipResizing( @@ -117,29 +131,41 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override public void onBackPressed() { + if (DEBUG) Log.d(TAG, "onBackPressed()"); + restorePipAndFinish(); } @Override - public void onPipEntered() { } + public void onPipEntered() { + if (DEBUG) Log.d(TAG, "onPipEntered()"); + } @Override public void onPipActivityClosed() { + if (DEBUG) Log.d(TAG, "onPipActivityClosed()"); + finish(); } @Override public void onPipMenuActionsChanged(ParceledListSlice actions) { + if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()"); + boolean hasCustomActions = actions != null && !actions.getList().isEmpty(); mPipControlsViewController.setActions( hasCustomActions ? actions.getList() : Collections.EMPTY_LIST); } @Override - public void onShowPipMenu() { } + public void onShowPipMenu() { + if (DEBUG) Log.d(TAG, "onShowPipMenu()"); + } @Override public void onMoveToFullscreen() { + if (DEBUG) Log.d(TAG, "onMoveToFullscreen()"); + // Moving PIP to fullscreen is implemented by resizing PINNED_STACK with null bounds. // This conflicts with restoring PIP position, so disable it. mRestorePipSizeWhenClose = false; @@ -148,8 +174,17 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { @Override public void onPipResizeAboutToStart() { + if (DEBUG) Log.d(TAG, "onPipResizeAboutToStart()"); + finish(); mPipManager.suspendPipResizing( PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH); } + + @Override + public void finish() { + if (DEBUG) Log.d(TAG, "finish()", new RuntimeException()); + + super.finish(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt new file mode 100644 index 000000000000..d65b285adb0c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/RelativeTouchListener.kt @@ -0,0 +1,162 @@ +/* + * 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.util + +import android.graphics.PointF +import android.os.Handler +import android.view.MotionEvent +import android.view.VelocityTracker +import android.view.View +import android.view.ViewConfiguration +import kotlin.math.hypot + +/** + * Listener which receives [onDown], [onMove], and [onUp] events, with relevant information about + * the coordinates of the touch and the view relative to the initial ACTION_DOWN event and the + * view's initial position. + */ +abstract class RelativeTouchListener : View.OnTouchListener { + + /** + * Called when an ACTION_DOWN event is received for the given view. + * + * @return False if the object is not interested in MotionEvents at this time, or true if we + * should consume this event and subsequent events, and begin calling [onMove]. + */ + abstract fun onDown(v: View, ev: MotionEvent): Boolean + + /** + * Called when an ACTION_MOVE event is received for the given view. This signals that the view + * is being dragged. + * + * @param viewInitialX The view's translationX value when this touch gesture started. + * @param viewInitialY The view's translationY value when this touch gesture started. + * @param dx Horizontal distance covered since the initial ACTION_DOWN event, in pixels. + * @param dy Vertical distance covered since the initial ACTION_DOWN event, in pixels. + */ + abstract fun onMove( + v: View, + ev: MotionEvent, + viewInitialX: Float, + viewInitialY: Float, + dx: Float, + dy: Float + ) + + /** + * Called when an ACTION_UP event is received for the given view. This signals that a drag or + * fling gesture has completed. + * + * @param viewInitialX The view's translationX value when this touch gesture started. + * @param viewInitialY The view's translationY value when this touch gesture started. + * @param dx Horizontal distance covered, in pixels. + * @param dy Vertical distance covered, in pixels. + * @param velX The final horizontal velocity of the gesture, in pixels/second. + * @param velY The final vertical velocity of the gesture, in pixels/second. + */ + abstract fun onUp( + v: View, + ev: MotionEvent, + viewInitialX: Float, + viewInitialY: Float, + dx: Float, + dy: Float, + velX: Float, + velY: Float + ) + + /** The raw coordinates of the last ACTION_DOWN event. */ + private val touchDown = PointF() + + /** The coordinates of the view, at the time of the last ACTION_DOWN event. */ + private val viewPositionOnTouchDown = PointF() + + private val velocityTracker = VelocityTracker.obtain() + + private var touchSlop: Int = -1 + private var movedEnough = false + + private val handler = Handler() + private var performedLongClick = false + + @Suppress("UNCHECKED_CAST") + override fun onTouch(v: View, ev: MotionEvent): Boolean { + addMovement(ev) + + val dx = ev.rawX - touchDown.x + val dy = ev.rawY - touchDown.y + + when (ev.action) { + MotionEvent.ACTION_DOWN -> { + if (!onDown(v, ev)) { + return false + } + + // Grab the touch slop, it might have changed if the config changed since the + // last gesture. + touchSlop = ViewConfiguration.get(v.context).scaledTouchSlop + + touchDown.set(ev.rawX, ev.rawY) + viewPositionOnTouchDown.set(v.translationX, v.translationY) + + performedLongClick = false + handler.postDelayed({ + performedLongClick = v.performLongClick() + }, ViewConfiguration.getLongPressTimeout().toLong()) + } + + MotionEvent.ACTION_MOVE -> { + if (!movedEnough && hypot(dx, dy) > touchSlop && !performedLongClick) { + movedEnough = true + handler.removeCallbacksAndMessages(null) + } + + if (movedEnough) { + onMove(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy) + } + } + + MotionEvent.ACTION_UP -> { + if (movedEnough) { + velocityTracker.computeCurrentVelocity(1000 /* units */) + onUp(v, ev, viewPositionOnTouchDown.x, viewPositionOnTouchDown.y, dx, dy, + velocityTracker.xVelocity, velocityTracker.yVelocity) + } else if (!performedLongClick) { + v.performClick() + } else { + handler.removeCallbacksAndMessages(null) + } + + velocityTracker.clear() + movedEnough = false + } + } + + return true + } + + /** + * Adds a movement to the velocity tracker using raw screen coordinates. + */ + private fun addMovement(event: MotionEvent) { + val deltaX = event.rawX - event.x + val deltaY = event.rawY - event.y + event.offsetLocation(deltaX, deltaY) + velocityTracker.addMovement(event) + event.offsetLocation(-deltaX, -deltaY) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java index cf1299fc66e0..ce032c94deab 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -115,6 +115,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { @Test public void testShow_dismissedByCallback() throws Exception { + doAnswer(answerVoid(Runnable::run)).when(mHandler).post(any(Runnable.class)); doAnswer(invocation -> { IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1]; callback.onDismiss(); @@ -184,7 +185,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private void verifyViewDismissed(SurfaceView v) throws Exception { verify(mParent).removeView(v); - verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID); + verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true); assertThat(mContext.isBound(mComponentName)).isFalse(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index 037f04ec1d7c..e472de349466 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -374,7 +374,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); - mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); assertFalse(mNotificationShadeWindowController.getBubblesShowing()); verify(mNotificationEntryManager, times(3)).updateNotifications(any()); assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey())); @@ -399,7 +399,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); assertTrue(mNotificationShadeWindowController.getBubbleExpanded()); @@ -436,7 +436,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); @@ -448,7 +448,7 @@ public class BubbleControllerTest extends SysuiTestCase { mRow2.getEntry())); // Switch which bubble is expanded - mBubbleController.selectBubble(mRow.getEntry().getKey()); + mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey())); mBubbleData.setExpanded(true); assertEquals(mRow.getEntry(), mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry()); @@ -482,7 +482,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot()); // Expand - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); @@ -510,7 +510,7 @@ public class BubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot()); // Expand - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); @@ -544,7 +544,7 @@ public class BubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mSysUiStateBubblesExpanded); @@ -726,7 +726,7 @@ public class BubbleControllerTest extends SysuiTestCase { public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException { mBubbleController.updateBubble(mRow.getEntry()); mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(2)).send(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java index 545de210d5b5..5f4f2ef04c1d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -321,7 +321,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey())); assertTrue(mBubbleController.hasBubbles()); - mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); assertFalse(mNotificationShadeWindowController.getBubblesShowing()); verify(mNotifCallback, times(3)).invalidateNotifications(anyString()); assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey())); @@ -344,7 +344,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Expand the stack BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); assertTrue(mNotificationShadeWindowController.getBubbleExpanded()); @@ -376,7 +376,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); @@ -385,7 +385,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry())); // Switch which bubble is expanded - mBubbleController.selectBubble(mRow.getEntry().getKey()); + mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey())); mBubbleData.setExpanded(true); assertEquals(mRow.getEntry(), mBubbleData.getBubbleWithKey(stackView.getExpandedBubble().getKey()).getEntry()); @@ -416,7 +416,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot()); // Expand - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); @@ -442,7 +442,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot()); // Expand - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey()); @@ -474,7 +474,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { // Expand BubbleStackView stackView = mBubbleController.getStackView(); - mBubbleController.expandStack(); + mBubbleData.setExpanded(true); assertTrue(mBubbleController.isStackExpanded()); verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey()); @@ -628,7 +628,7 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException { mBubbleController.updateBubble(mRow.getEntry()); mBubbleController.updateBubble(mRow2.getEntry()); - mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE); + mBubbleData.dismissAll(BubbleController.DISMISS_USER_GESTURE); verify(mDeleteIntent, times(2)).send(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt index 260f52070a70..d407b8a1e449 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt @@ -67,10 +67,11 @@ public class SeekBarObserverTest : SysuiTestCase() { val isEnabled = false val data = SeekBarViewModel.Progress(isEnabled, false, null, null, null) observer.onChanged(data) - // THEN seek bar visibility is set to GONE - assertThat(seekBarView.getVisibility()).isEqualTo(View.GONE) - assertThat(elapsedTimeView.getVisibility()).isEqualTo(View.GONE) - assertThat(totalTimeView.getVisibility()).isEqualTo(View.GONE) + // THEN seek bar shows just a line with no text + assertThat(seekBarView.isEnabled()).isFalse() + assertThat(seekBarView.getThumb().getAlpha()).isEqualTo(0) + assertThat(elapsedTimeView.getText()).isEqualTo("") + assertThat(totalTimeView.getText()).isEqualTo("") } @Test diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index b3a30abca6f1..6695eed0ff65 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -33,7 +33,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; @@ -72,8 +71,6 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import java.util.ArrayList; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @SmallTest @@ -255,19 +252,16 @@ public final class EntitlementManagerTest { @Test public void testRequestLastEntitlementCacheValue() throws Exception { - final CountDownLatch mCallbacklatch = new CountDownLatch(1); // 1. Entitlement check is not required. mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR; ResultReceiver receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); @@ -277,12 +271,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 3. No cache value and ui entitlement check is needed. @@ -291,12 +283,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(1, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 4. Cache value is TETHER_ERROR_PROVISIONING_FAILED and don't need to run entitlement @@ -306,12 +296,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_PROVISIONING_FAILED, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 5. Cache value is TETHER_ERROR_PROVISIONING_FAILED and ui entitlement check is needed. @@ -320,12 +308,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(1, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 6. Cache value is TETHER_ERROR_NO_ERROR. @@ -334,12 +320,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_NO_ERROR, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 7. Test get value for other downstream type. @@ -347,12 +331,10 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); // 8. Test get value for invalid downstream type. @@ -361,22 +343,14 @@ public final class EntitlementManagerTest { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); - mCallbacklatch.countDown(); } }; mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI_P2P, receiver, true); mLooper.dispatchAll(); - callbackTimeoutHelper(mCallbacklatch); assertEquals(0, mEnMgr.uiProvisionCount); mEnMgr.reset(); } - void callbackTimeoutHelper(final CountDownLatch latch) throws Exception { - if (!latch.await(1, TimeUnit.SECONDS)) { - fail("Timout, fail to receive callback"); - } - } - @Test public void verifyPermissionResult() { setupForRequiredProvisioning(); diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 93bffe9f54e4..52a82dd2a156 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -36,10 +36,12 @@ import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.util.Slog; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.content.PackageMonitor; import libcore.io.IoUtils; @@ -261,22 +263,7 @@ public class WallpaperBackupAgent extends BackupAgent { // And reset to the wallpaper service we should be using ComponentName wpService = parseWallpaperComponent(infoStage, "wp"); - if (servicePackageExists(wpService)) { - Slog.i(TAG, "Using wallpaper service " + wpService); - mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM); - if (!lockImageStage.exists()) { - // We have a live wallpaper and no static lock image, - // allow live wallpaper to show "through" on lock screen. - mWm.clear(FLAG_LOCK); - } - } else { - // If we've restored a live wallpaper, but the component doesn't exist, - // we should log it as an error so we can easily identify the problem - // in reports from users - if (wpService != null) { - Slog.e(TAG, "Wallpaper service " + wpService + " isn't available."); - } - } + updateWallpaperComponent(wpService, !lockImageStage.exists()); } catch (Exception e) { Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage()); } finally { @@ -293,6 +280,28 @@ public class WallpaperBackupAgent extends BackupAgent { } } + @VisibleForTesting + void updateWallpaperComponent(ComponentName wpService, boolean applyToLock) throws IOException { + if (servicePackageExists(wpService)) { + Slog.i(TAG, "Using wallpaper service " + wpService); + mWm.setWallpaperComponent(wpService, UserHandle.USER_SYSTEM); + if (applyToLock) { + // We have a live wallpaper and no static lock image, + // allow live wallpaper to show "through" on lock screen. + mWm.clear(FLAG_LOCK); + } + } else { + // If we've restored a live wallpaper, but the component doesn't exist, + // we should log it as an error so we can easily identify the problem + // in reports from users + if (wpService != null) { + applyComponentAtInstall(wpService, applyToLock); + Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. " + + " Will try to apply later"); + } + } + } + private void restoreFromStage(File stage, File info, String hintTag, int which) throws IOException { if (stage.exists()) { @@ -372,7 +381,8 @@ public class WallpaperBackupAgent extends BackupAgent { return (value == null) ? defValue : Integer.parseInt(value); } - private boolean servicePackageExists(ComponentName comp) { + @VisibleForTesting + boolean servicePackageExists(ComponentName comp) { try { if (comp != null) { final IPackageManager pm = AppGlobals.getPackageManager(); @@ -401,4 +411,53 @@ public class WallpaperBackupAgent extends BackupAgent { throws IOException { // Intentionally blank } + + private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock) { + PackageMonitor packageMonitor = getWallpaperPackageMonitor(componentName, applyToLock); + packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true); + } + + @VisibleForTesting + PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock) { + return new PackageMonitor() { + @Override + public void onPackageAdded(String packageName, int uid) { + if (!isDeviceInRestore()) { + // We don't want to reapply the wallpaper outside a restore. + unregister(); + return; + } + + if (componentName.getPackageName().equals(packageName)) { + Slog.d(TAG, "Applying component " + componentName); + mWm.setWallpaperComponent(componentName); + if (applyToLock) { + try { + mWm.clear(FLAG_LOCK); + } catch (IOException e) { + Slog.w(TAG, "Failed to apply live wallpaper to lock screen: " + e); + } + } + // We're only expecting to restore the wallpaper component once. + unregister(); + } + } + }; + } + + @VisibleForTesting + boolean isDeviceInRestore() { + try { + boolean isInSetup = Settings.Secure.getInt(getBaseContext().getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE) == 0; + boolean isInDeferredSetup = Settings.Secure.getInt(getBaseContext() + .getContentResolver(), + Settings.Secure.USER_SETUP_PERSONALIZATION_STATE) == + Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED; + return isInSetup || isInDeferredSetup; + } catch (Settings.SettingNotFoundException e) { + Slog.w(TAG, "Failed to check if the user is in restore: " + e); + return false; + } + } }
\ No newline at end of file diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java index 255fdef36cd7..4367075abc74 100644 --- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java +++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wallpaperbackup.tests; +package com.android.wallpaperbackup; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; @@ -26,17 +26,22 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.WallpaperManager; import android.app.backup.FullBackupDataOutput; +import android.content.ComponentName; +import android.content.Context; import android.content.SharedPreferences; import android.os.UserHandle; import androidx.test.core.app.ApplicationProvider; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.content.PackageMonitor; import com.android.wallpaperbackup.WallpaperBackupAgent; import com.android.wallpaperbackup.utils.ContextWithServiceOverrides; @@ -58,6 +63,7 @@ import java.util.List; public class WallpaperBackupAgentTest { private static final String SYSTEM_GENERATION = "system_gen"; private static final String LOCK_GENERATION = "lock_gen"; + private static final String TEST_WALLPAPER_PACKAGE = "wallpaper_package"; private static final int TEST_SYSTEM_WALLPAPER_ID = 1; private static final int TEST_LOCK_WALLPAPER_ID = 2; @@ -66,11 +72,13 @@ public class WallpaperBackupAgentTest { @Mock private WallpaperManager mWallpaperManager; @Mock private SharedPreferences mSharedPreferences; @Mock private SharedPreferences.Editor mSharedPreferenceEditor; + @Mock private Context mMockContext; @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); private ContextWithServiceOverrides mContext; private IsolatedWallpaperBackupAgent mWallpaperBackupAgent; + private ComponentName mWallpaperComponent; @Before public void setUp() { @@ -88,6 +96,8 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent = new IsolatedWallpaperBackupAgent(mTemporaryFolder.getRoot()); mWallpaperBackupAgent.attach(mContext); mWallpaperBackupAgent.onCreate(); + + mWallpaperComponent = new ComponentName(TEST_WALLPAPER_PACKAGE, ""); } @Test @@ -130,6 +140,69 @@ public class WallpaperBackupAgentTest { inOrder.verify(mSharedPreferenceEditor).apply(); } + @Test + public void updateWallpaperComponent_doesApplyLater() throws IOException { + mWallpaperBackupAgent.mIsDeviceInRestore = true; + + mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + /* applyToLock */ true); + + // Imitate wallpaper component installation. + mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, + /* uid */0); + + verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); + verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK)); + } + + @Test + public void updateWallpaperComponent_applyToLockFalse_doesApplyLaterOnlyToMainScreen() + throws IOException { + mWallpaperBackupAgent.mIsDeviceInRestore = true; + + mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + /* applyToLock */ false); + + // Imitate wallpaper component installation. + mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, + /* uid */0); + + verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); + verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK)); + } + + @Test + public void updateWallpaperComponent_deviceNotInRestore_doesNotApply() + throws IOException { + mWallpaperBackupAgent.mIsDeviceInRestore = false; + + mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + /* applyToLock */ true); + + // Imitate wallpaper component installation. + mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, + /* uid */0); + + verify(mWallpaperManager, never()).setWallpaperComponent(mWallpaperComponent); + verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK)); + } + + @Test + public void updateWallpaperComponent_differentPackageInstalled_doesNotApply() + throws IOException { + mWallpaperBackupAgent.mIsDeviceInRestore = false; + + mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + /* applyToLock */ true); + + // Imitate "wrong" wallpaper component installation. + mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(/* packageName */"", + /* uid */0); + + verify(mWallpaperManager, never()).setWallpaperComponent(mWallpaperComponent); + verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK)); + } + private void mockUnbackedUpState() { mockCurrentWallpapers(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID); when(mSharedPreferences.getInt(eq(SYSTEM_GENERATION), eq(-1))).thenReturn(-1); @@ -162,6 +235,8 @@ public class WallpaperBackupAgentTest { private class IsolatedWallpaperBackupAgent extends WallpaperBackupAgent { File mWallpaperBaseDirectory; List<File> mBackedUpFiles = new ArrayList<>(); + PackageMonitor mWallpaperPackageMonitor; + boolean mIsDeviceInRestore = false; IsolatedWallpaperBackupAgent(File wallpaperBaseDirectory) { mWallpaperBaseDirectory = wallpaperBaseDirectory; @@ -181,5 +256,27 @@ public class WallpaperBackupAgentTest { public SharedPreferences getSharedPreferences(File file, int mode) { return mSharedPreferences; } + + @Override + boolean servicePackageExists(ComponentName comp) { + return false; + } + + @Override + boolean isDeviceInRestore() { + return mIsDeviceInRestore; + } + + @Override + PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, + boolean applyToLock) { + mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, applyToLock); + return mWallpaperPackageMonitor; + } + + @Override + public Context getBaseContext() { + return mMockContext; + } } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java index ff59c24a7ca2..20a11bd9acd3 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java @@ -17,6 +17,7 @@ package com.android.server.accessibility; import android.annotation.NonNull; +import android.app.ActivityManager; import android.os.ShellCommand; import android.os.UserHandle; @@ -83,7 +84,7 @@ final class AccessibilityShellCommand extends ShellCommand { return null; } } - return UserHandle.USER_SYSTEM; + return ActivityManager.getCurrentUser(); } @Override diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index a9c3079adcc0..7f25de6b3470 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1836,12 +1836,13 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void requestLocationUpdates(LocationRequest request, ILocationListener listener, - PendingIntent intent, String packageName, String featureId) { + PendingIntent intent, String packageName, String featureId, String listenerId) { if (request == null) { request = DEFAULT_LOCATION_REQUEST; } - CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId); + CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, featureId, + listenerId); identity.enforceLocationPermission(); WorkSource workSource = request.getWorkSource(); @@ -2027,7 +2028,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean getCurrentLocation(LocationRequest locationRequest, ICancellationSignal remoteCancellationSignal, ILocationListener listener, - String packageName, String featureId) { + String packageName, String featureId, String listenerId) { // side effect of validating locationRequest and packageName Location lastLocation = getLastLocation(locationRequest, packageName, featureId); if (lastLocation != null) { @@ -2052,7 +2053,7 @@ public class LocationManagerService extends ILocationManager.Stub { } } - requestLocationUpdates(locationRequest, listener, null, packageName, featureId); + requestLocationUpdates(locationRequest, listener, null, packageName, featureId, listenerId); CancellationSignal cancellationSignal = CancellationSignal.fromTransport( remoteCancellationSignal); if (cancellationSignal != null) { diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java index bc455338fc52..8f8990fdaae7 100644 --- a/services/core/java/com/android/server/am/AnrHelper.java +++ b/services/core/java/com/android/server/am/AnrHelper.java @@ -98,7 +98,7 @@ class AnrHelper { final long endTime = SystemClock.uptimeMillis(); Slog.d(TAG, "Completed ANR of " + r.mApp.processName + " in " + (endTime - startTime) + "ms, latency " + reportLatency - + (onlyDumpSelf ? "ms" : "ms (expired, only dump ANR app)")); + + (onlyDumpSelf ? "ms (expired, only dump ANR app)" : "ms")); } mRunning.set(false); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index dbcb3da3e2f4..2d6ef81faf1c 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -231,7 +231,8 @@ public final class OomAdjuster { final ServiceThread adjusterThread = new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST, false /* allowIo */); adjusterThread.start(); - Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP); + adjusterThread.getThreadHandler().post(() -> Process.setThreadGroupAndCpuset( + adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP)); return adjusterThread; } diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 4431abe43136..808f8c21cc8d 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -558,8 +558,10 @@ public abstract class BiometricServiceBase extends SystemService FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT); + ClientMonitor newClient = mPendingClient; mCurrentClient = null; - startClient(mPendingClient, false); + mPendingClient = null; + startClient(newClient, false); } } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 962f337a8b3f..9a910bf5e859 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -569,9 +569,10 @@ public final class ContentService extends IContentService.Stub { // Immediately dispatch notifications to foreground apps that // are important to the user; all other background observers are // delayed to avoid stampeding + final boolean noDelay = (key.flags & ContentResolver.NOTIFY_NO_DELAY) != 0; final int procState = LocalServices.getService(ActivityManagerInternal.class) .getUidProcessState(key.uid); - if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || noDelay) { task.run(); } else { BackgroundThread.getHandler().postDelayed(task, BACKGROUND_OBSERVER_DELAY); diff --git a/services/core/java/com/android/server/location/AppOpsHelper.java b/services/core/java/com/android/server/location/AppOpsHelper.java index 9c279166ac8a..cb64c50bf11d 100644 --- a/services/core/java/com/android/server/location/AppOpsHelper.java +++ b/services/core/java/com/android/server/location/AppOpsHelper.java @@ -191,7 +191,7 @@ public class AppOpsHelper { callerIdentity.uid, callerIdentity.packageName, callerIdentity.featureId, - null) == AppOpsManager.MODE_ALLOWED; + callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } @@ -210,7 +210,7 @@ public class AppOpsHelper { callerIdentity.packageName, false, callerIdentity.featureId, - null) == AppOpsManager.MODE_ALLOWED; + callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } @@ -245,7 +245,7 @@ public class AppOpsHelper { callerIdentity.uid, callerIdentity.packageName, callerIdentity.featureId, - null) == AppOpsManager.MODE_ALLOWED; + callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java index b84fd13415fc..8d508bb6256d 100644 --- a/services/core/java/com/android/server/location/CallerIdentity.java +++ b/services/core/java/com/android/server/location/CallerIdentity.java @@ -83,12 +83,22 @@ public final class CallerIdentity { */ public static CallerIdentity fromBinder(Context context, String packageName, @Nullable String featureId) { + return fromBinder(context, packageName, featureId, null); + } + + /** + * Creates a CallerIdentity from the current binder identity, using the given package, feature + * id, and listener id. The package will be checked to enforce it belongs to the calling uid, + * and a security exception will be thrown if it is invalid. + */ + public static CallerIdentity fromBinder(Context context, String packageName, + @Nullable String featureId, @Nullable String listenerId) { int uid = Binder.getCallingUid(); if (!ArrayUtils.contains(context.getPackageManager().getPackagesForUid(uid), packageName)) { throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid); } - return fromBinderUnsafe(context, packageName, featureId); + return fromBinderUnsafe(context, packageName, featureId, listenerId); } /** @@ -99,8 +109,19 @@ public final class CallerIdentity { */ public static CallerIdentity fromBinderUnsafe(Context context, String packageName, @Nullable String featureId) { + return fromBinderUnsafe(context, packageName, featureId, null); + } + + /** + * Creates a CallerIdentity from the current binder identity, using the given package, feature + * id, and listener id. The package will not be checked to enforce that it belongs to the + * calling uid - this method should only be used if the package will be validated by some other + * means, such as an appops call. + */ + public static CallerIdentity fromBinderUnsafe(Context context, String packageName, + @Nullable String featureId, @Nullable String listenerId) { return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), - UserHandle.getCallingUserId(), packageName, featureId, + UserHandle.getCallingUserId(), packageName, featureId, listenerId, getBinderPermissionLevel(context)); } @@ -157,6 +178,9 @@ public final class CallerIdentity { /** The calling feature id. */ public final @Nullable String featureId; + /** The calling listener id. */ + public final @Nullable String listenerId; + /** * The calling location permission level. This field should only be used for validating * permissions for API access. It should not be used for validating permissions for location @@ -167,11 +191,18 @@ public final class CallerIdentity { @VisibleForTesting public CallerIdentity(int uid, int pid, int userId, String packageName, @Nullable String featureId, @PermissionLevel int permissionLevel) { + this(uid, pid, userId, packageName, featureId, null, permissionLevel); + } + + private CallerIdentity(int uid, int pid, int userId, String packageName, + @Nullable String featureId, @Nullable String listenerId, + @PermissionLevel int permissionLevel) { this.uid = uid; this.pid = pid; this.userId = userId; this.packageName = Objects.requireNonNull(packageName); this.featureId = featureId; + this.listenerId = listenerId; this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE, PERMISSION_FINE, "permissionLevel"); } @@ -216,7 +247,8 @@ public final class CallerIdentity { return uid == that.uid && pid == that.pid && packageName.equals(that.packageName) - && Objects.equals(featureId, that.featureId); + && Objects.equals(featureId, that.featureId) + && Objects.equals(listenerId, that.listenerId); } @Override diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 193107996d1e..c3413e8d2934 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -98,18 +98,26 @@ class MediaRouter2ServiceImpl { public List<MediaRoute2Info> getSystemRoutes() { final int uid = Binder.getCallingUid(); final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); + final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_AUDIO_ROUTING) + == PackageManager.PERMISSION_GRANTED; final long token = Binder.clearCallingIdentity(); try { Collection<MediaRoute2Info> systemRoutes; synchronized (mLock) { UserRecord userRecord = getOrCreateUserRecordLocked(userId); - MediaRoute2ProviderInfo providerInfo = - userRecord.mHandler.mSystemProvider.getProviderInfo(); - if (providerInfo != null) { - systemRoutes = providerInfo.getRoutes(); + if (hasModifyAudioRoutingPermission) { + MediaRoute2ProviderInfo providerInfo = + userRecord.mHandler.mSystemProvider.getProviderInfo(); + if (providerInfo != null) { + systemRoutes = providerInfo.getRoutes(); + } else { + systemRoutes = Collections.emptyList(); + } } else { - systemRoutes = Collections.emptyList(); + systemRoutes = new ArrayList<>(); + systemRoutes.add(userRecord.mHandler.mSystemProvider.getDefaultRoute()); } } return new ArrayList<>(systemRoutes); @@ -122,18 +130,25 @@ class MediaRouter2ServiceImpl { public RoutingSessionInfo getSystemSessionInfo() { final int uid = Binder.getCallingUid(); final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); + final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_AUDIO_ROUTING) + == PackageManager.PERMISSION_GRANTED; final long token = Binder.clearCallingIdentity(); try { RoutingSessionInfo systemSessionInfo = null; synchronized (mLock) { UserRecord userRecord = getOrCreateUserRecordLocked(userId); - List<RoutingSessionInfo> sessionInfos = - userRecord.mHandler.mSystemProvider.getSessionInfos(); - if (sessionInfos != null && !sessionInfos.isEmpty()) { - systemSessionInfo = sessionInfos.get(0); + List<RoutingSessionInfo> sessionInfos; + if (hasModifyAudioRoutingPermission) { + sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos(); + if (sessionInfos != null && !sessionInfos.isEmpty()) { + systemSessionInfo = sessionInfos.get(0); + } else { + Slog.w(TAG, "System provider does not have any session info."); + } } else { - Slog.w(TAG, "System provider does not have any session info."); + systemSessionInfo = userRecord.mHandler.mSystemProvider.getDefaultSessionInfo(); } } return systemSessionInfo; @@ -654,10 +669,20 @@ class MediaRouter2ServiceImpl { return; } - routerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::transferToRouteOnHandler, - routerRecord.mUserRecord.mHandler, - DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); + String defaultRouteId = + routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId(); + if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission + && !TextUtils.equals(route.getId(), defaultRouteId)) { + routerRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::notifySessionCreationFailedToRouter, + routerRecord.mUserRecord.mHandler, + routerRecord, toOriginalRequestId(DUMMY_REQUEST_ID))); + } else { + routerRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::transferToRouteOnHandler, + routerRecord.mUserRecord.mHandler, + DUMMY_REQUEST_ID, routerRecord, uniqueSessionId, route)); + } } private void setSessionVolumeWithRouter2Locked(@NonNull IMediaRouter2 router, @@ -1185,18 +1210,42 @@ class MediaRouter2ServiceImpl { } } - List<IMediaRouter2> routers = getRouters(); + List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true); + List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false); List<IMediaRouter2Manager> managers = getManagers(); + List<MediaRoute2Info> defaultRoute = new ArrayList<>(); + defaultRoute.add(mSystemProvider.getDefaultRoute()); + if (addedRoutes.size() > 0) { - notifyRoutesAddedToRouters(routers, addedRoutes); + notifyRoutesAddedToRouters(routersWithModifyAudioRoutingPermission, addedRoutes); + if (!provider.mIsSystemRouteProvider) { + notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission, + addedRoutes); + } else if (prevInfo == null) { + notifyRoutesAddedToRouters(routersWithoutModifyAudioRoutingPermission, + defaultRoute); + } // 'else' is handled as changed routes notifyRoutesAddedToManagers(managers, addedRoutes); } if (removedRoutes.size() > 0) { - notifyRoutesRemovedToRouters(routers, removedRoutes); + notifyRoutesRemovedToRouters(routersWithModifyAudioRoutingPermission, + removedRoutes); + if (!provider.mIsSystemRouteProvider) { + notifyRoutesRemovedToRouters(routersWithoutModifyAudioRoutingPermission, + removedRoutes); + } notifyRoutesRemovedToManagers(managers, removedRoutes); } if (changedRoutes.size() > 0) { - notifyRoutesChangedToRouters(routers, changedRoutes); + notifyRoutesChangedToRouters(routersWithModifyAudioRoutingPermission, + changedRoutes); + if (!provider.mIsSystemRouteProvider) { + notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission, + changedRoutes); + } else if (prevInfo != null) { + notifyRoutesChangedToRouters(routersWithoutModifyAudioRoutingPermission, + defaultRoute); + } // 'else' is handled as added routes notifyRoutesChangedToManagers(managers, changedRoutes); } } @@ -1223,6 +1272,15 @@ class MediaRouter2ServiceImpl { toOriginalRequestId(uniqueRequestId)); return; } + if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission + && !TextUtils.equals(route.getId(), + mSystemProvider.getDefaultRoute().getId())) { + Slog.w(TAG, "MODIFY_AUDIO_ROUTING permission is required to transfer to" + + route); + notifySessionCreationFailedToRouter(routerRecord, + toOriginalRequestId(uniqueRequestId)); + return; + } SessionCreationRequest request = new SessionCreationRequest(routerRecord, uniqueRequestId, route, managerRecord); @@ -1444,7 +1502,9 @@ class MediaRouter2ServiceImpl { if (service == null) { return; } - notifySessionInfoChangedToRouters(getRouters(), sessionInfo); + notifySessionInfoChangedToRouters(getRouters(true), sessionInfo); + notifySessionInfoChangedToRouters(getRouters(false), + mSystemProvider.getDefaultSessionInfo()); return; } @@ -1565,7 +1625,7 @@ class MediaRouter2ServiceImpl { } } - private List<IMediaRouter2> getRouters() { + private List<IMediaRouter2> getAllRouters() { final List<IMediaRouter2> routers = new ArrayList<>(); MediaRouter2ServiceImpl service = mServiceRef.get(); if (service == null) { @@ -1579,6 +1639,23 @@ class MediaRouter2ServiceImpl { return routers; } + private List<IMediaRouter2> getRouters(boolean hasModifyAudioRoutingPermission) { + final List<IMediaRouter2> routers = new ArrayList<>(); + MediaRouter2ServiceImpl service = mServiceRef.get(); + if (service == null) { + return routers; + } + synchronized (service.mLock) { + for (RouterRecord routerRecord : mUserRecord.mRouterRecords) { + if (hasModifyAudioRoutingPermission + == routerRecord.mHasModifyAudioRoutingPermission) { + routers.add(routerRecord.mRouter); + } + } + } + return routers; + } + private List<IMediaRouter2Manager> getManagers() { final List<IMediaRouter2Manager> managers = new ArrayList<>(); MediaRouter2ServiceImpl service = mServiceRef.get(); diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 5b16d686e04c..6e2feeb15e21 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -72,6 +72,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // This should be the currently selected route. MediaRoute2Info mDefaultRoute; MediaRoute2Info mDeviceRoute; + RoutingSessionInfo mDefaultSessionInfo; final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { @@ -114,6 +115,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }); updateSessionInfosIfNeeded(); + mContext.registerReceiver(new VolumeChangeReceiver(), new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION)); @@ -156,6 +158,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @Override public void transferToRoute(long requestId, String sessionId, String routeId) { + if (TextUtils.equals(routeId, DEFAULT_ROUTE_ID)) { + // The currently selected route is the default route. + return; + } if (mBtRouteProvider != null) { if (TextUtils.equals(routeId, mDeviceRoute.getId())) { mBtRouteProvider.transferTo(null); @@ -182,6 +188,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { return mDefaultRoute; } + public RoutingSessionInfo getDefaultSessionInfo() { + return mDefaultSessionInfo; + } + private void updateDeviceRoute(AudioRoutesInfo newRoutes) { int name = R.string.default_audio_route_name; if (newRoutes != null) { @@ -229,8 +239,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { */ boolean updateSessionInfosIfNeeded() { synchronized (mLock) { - // Prevent to execute this method before mBtRouteProvider is created. - if (mBtRouteProvider == null) return false; RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get( 0); @@ -238,14 +246,19 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SYSTEM_SESSION_ID, "" /* clientPackageName */) .setSystemSession(true); - MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute(); - if (selectedRoute == null) { - selectedRoute = mDeviceRoute; - } else { - builder.addTransferableRoute(mDeviceRoute.getId()); + MediaRoute2Info selectedRoute = mDeviceRoute; + if (mBtRouteProvider != null) { + MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute(); + if (selectedBtRoute != null) { + selectedRoute = selectedBtRoute; + builder.addTransferableRoute(mDeviceRoute.getId()); + } } mSelectedRouteId = selectedRoute.getId(); - mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute).build(); + mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute) + .setSystemRoute(true) + .setProviderId(mUniqueId) + .build(); builder.addSelectedRoute(mSelectedRouteId); for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) { @@ -258,6 +271,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } else { mSessionInfos.clear(); mSessionInfos.add(newSessionInfo); + mDefaultSessionInfo = new RoutingSessionInfo.Builder( + SYSTEM_SESSION_ID, "" /* clientPackageName */) + .setProviderId(mUniqueId) + .setSystemSession(true) + .addSelectedRoute(DEFAULT_ROUTE_ID) + .build(); return true; } } @@ -302,6 +321,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } else if (mBtRouteProvider != null) { mBtRouteProvider.setSelectedRouteVolume(newVolume); } + mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute) + .setVolume(newVolume) + .build(); publishProviderState(); } } diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 1e4dc7bfe778..daf4bf271ca2 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -399,7 +399,9 @@ public abstract class ApexManager { @VisibleForTesting protected IApexService waitForApexService() { try { - return IApexService.Stub.asInterface(Binder.waitForService("apexservice")); + // Since apexd is a trusted platform component, synchronized calls are allowable + return IApexService.Stub.asInterface( + Binder.allowBlocking(Binder.waitForService("apexservice"))); } catch (RemoteException e) { throw new IllegalStateException("Required service apexservice not available"); } diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index ba7583fe7f7a..dab4bfd4df5a 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -46,6 +46,7 @@ import com.android.server.pm.dex.DexoptOptions; import java.io.File; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -106,6 +107,8 @@ public class BackgroundDexOptService extends JobService { private static final long mDowngradeUnusedAppsThresholdInMillis = getDowngradeUnusedAppsThresholdInMillis(); + private static List<PackagesUpdatedListener> sPackagesUpdatedListeners = new ArrayList<>(); + public static void schedule(Context context) { if (isBackgroundDexoptDisabled()) { return; @@ -244,6 +247,7 @@ public class BackgroundDexOptService extends JobService { } } notifyPinService(updatedPackages); + notifyPackagesUpdated(updatedPackages); // Ran to completion, so we abandon our timeslice and do not reschedule. jobFinished(jobParams, /* reschedule */ false); } @@ -391,6 +395,7 @@ public class BackgroundDexOptService extends JobService { } finally { // Always let the pinner service know about changes. notifyPinService(updatedPackages); + notifyPackagesUpdated(updatedPackages); } } @@ -642,6 +647,32 @@ public class BackgroundDexOptService extends JobService { } } + public static interface PackagesUpdatedListener { + /** Callback when packages have been updated by the bg-dexopt service. */ + public void onPackagesUpdated(ArraySet<String> updatedPackages); + } + + public static void addPackagesUpdatedListener(PackagesUpdatedListener listener) { + synchronized (sPackagesUpdatedListeners) { + sPackagesUpdatedListeners.add(listener); + } + } + + public static void removePackagesUpdatedListener(PackagesUpdatedListener listener) { + synchronized (sPackagesUpdatedListeners) { + sPackagesUpdatedListeners.remove(listener); + } + } + + /** Notify all listeners (#addPackagesUpdatedListener) that packages have been updated. */ + private void notifyPackagesUpdated(ArraySet<String> updatedPackages) { + synchronized (sPackagesUpdatedListeners) { + for (PackagesUpdatedListener listener : sPackagesUpdatedListeners) { + listener.onPackagesUpdated(updatedPackages); + } + } + } + private static long getDowngradeUnusedAppsThresholdInMillis() { final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days"; String sysPropValue = SystemProperties.get(sysPropKey); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c47d2151a958..c4d034207449 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6484,11 +6484,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final Rect containingBounds = mTmpBounds; mCompatDisplayInsets.getContainerBounds(containingAppBounds, containingBounds, rotation, orientation, orientationRequested, canChangeOrientation); - resolvedBounds.set(containingAppBounds); + resolvedBounds.set(containingBounds); // The size of floating task is fixed (only swap), so the aspect ratio is already correct. if (!mCompatDisplayInsets.mIsFloating) { applyAspectRatio(resolvedBounds, containingAppBounds, containingBounds); } + // If the bounds are restricted by fixed aspect ratio, the resolved bounds should be put in + // the container app bounds. Otherwise the entire container bounds are available. + final boolean fillContainer = resolvedBounds.equals(containingBounds); + if (!fillContainer) { + // The horizontal position should not cover insets. + resolvedBounds.left = containingAppBounds.left; + } // Use resolvedBounds to compute other override configurations such as appBounds. The bounds // are calculated in compat container space. The actual position on screen will be applied @@ -6557,7 +6564,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final int offsetX = getHorizontalCenterOffset( (int) viewportW, (int) (contentW * mSizeCompatScale)); // Above coordinates are in "@" space, now place "*" and "#" to screen space. - final int screenPosX = parentAppBounds.left + offsetX; + final int screenPosX = (fillContainer ? parentBounds.left : parentAppBounds.left) + offsetX; final int screenPosY = parentBounds.top; if (screenPosX != 0 || screenPosY != 0) { if (mSizeCompatBounds != null) { @@ -7654,8 +7661,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Ensure the app bounds won't overlap with insets. Task.intersectWithInsetsIfFits(outAppBounds, outBounds, mNonDecorInsets[rotation]); } - // The horizontal position is centered and it should not cover insets. - outBounds.left = outAppBounds.left; } } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 1e5a924e1d4d..0470ffa6cdca 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1469,7 +1469,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (toDisplay.getDisplayId() != stack.getDisplayId()) { stack.reparent(toDisplay.getDefaultTaskDisplayArea(), false /* onTop */); } else { - toDisplay.mTaskContainers.positionStackAtBottom(stack); + toDisplay.getDefaultTaskDisplayArea().positionStackAtBottom(stack); } mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index edd14b7bebf3..a512337c96ec 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -54,7 +54,6 @@ import java.util.function.Predicate; * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks. * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks. * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container. - * Cannot have a sibling that is of type ANY. * * @param <T> type of the children of the DisplayArea. */ @@ -274,7 +273,6 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { ANY; static void checkSiblings(Type bottom, Type top) { - checkState(!(bottom == ANY && top == ANY), "ANY cannot be a sibling of ANY"); checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS), bottom + " must be above BELOW_TASKS"); checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS), diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 982157336295..15b6f9735bf1 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -16,9 +16,14 @@ package com.android.server.wm; +import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; + import android.content.res.Resources; import android.text.TextUtils; +import java.util.ArrayList; +import java.util.List; + /** * Policy that manages DisplayAreas. */ @@ -37,9 +42,9 @@ public abstract class DisplayAreaPolicy { protected final DisplayArea<? extends WindowContainer> mImeContainer; /** - * The Tasks container. Tasks etc. are automatically added to this container. + * The task display areas. Tasks etc. are automatically added to these containers. */ - protected final DisplayArea<? extends ActivityStack> mTaskContainers; + protected final List<TaskDisplayArea> mTaskDisplayAreas; /** * Construct a new {@link DisplayAreaPolicy} @@ -48,19 +53,19 @@ public abstract class DisplayAreaPolicy { * @param content the display content for which the policy applies * @param root the root display area under which the policy operates * @param imeContainer the ime container that the policy must attach - * @param taskDisplayArea the task container that the policy must attach + * @param taskDisplayAreas the task display areas that the policy must attach * * @see #attachDisplayAreas() */ protected DisplayAreaPolicy(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer, - DisplayArea<? extends ActivityStack> taskDisplayArea) { + List<TaskDisplayArea> taskDisplayAreas) { mWmService = wmService; mContent = content; mRoot = root; mImeContainer = imeContainer; - mTaskContainers = taskDisplayArea; + mTaskDisplayAreas = taskDisplayAreas; } /** @@ -80,15 +85,32 @@ public abstract class DisplayAreaPolicy { */ public abstract void addWindow(WindowToken token); + /** + * @return the number of task display areas on the display. + */ + public int getTaskDisplayAreaCount() { + return mTaskDisplayAreas.size(); + } + + /** + * @return the task display area at index. + */ + public TaskDisplayArea getTaskDisplayAreaAt(int index) { + return mTaskDisplayAreas.get(index); + } + /** Provider for platform-default display area policy. */ static final class DefaultProvider implements DisplayAreaPolicy.Provider { @Override public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, - DisplayArea<? extends WindowContainer> imeContainer, - TaskDisplayArea taskDisplayArea) { + DisplayArea<? extends WindowContainer> imeContainer) { + final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService, + "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER); + final List<TaskDisplayArea> tdaList = new ArrayList<>(); + tdaList.add(defaultTaskDisplayArea); return new DisplayAreaPolicyBuilder() - .build(wmService, content, root, imeContainer, taskDisplayArea); + .build(wmService, content, root, imeContainer, tdaList); } } @@ -107,8 +129,7 @@ public abstract class DisplayAreaPolicy { */ DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, - DisplayArea<? extends WindowContainer> imeContainer, - TaskDisplayArea taskDisplayArea); + DisplayArea<? extends WindowContainer> imeContainer); /** * Instantiate the device-specific {@link Provider}. diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index e8becfa27fac..2c2f43392eca 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -201,8 +201,8 @@ class DisplayAreaPolicyBuilder { Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer, - DisplayArea<? extends ActivityStack> taskDisplayArea, ArrayList<Feature> features) { - super(wmService, content, root, imeContainer, taskDisplayArea); + List<TaskDisplayArea> taskDisplayAreas, ArrayList<Feature> features) { + super(wmService, content, root, imeContainer, taskDisplayAreas); mFeatures = features; mAreas = new HashMap<>(features.size()); for (int i = 0; i < mFeatures.size(); i++) { @@ -267,7 +267,7 @@ class DisplayAreaPolicyBuilder { areaForLayer[layer].mChildren.add(leafArea); leafType = type; if (leafType == LEAF_TYPE_TASK_CONTAINERS) { - leafArea.mExisting = mTaskContainers; + addTaskDisplayAreasToLayer(areaForLayer[layer], layer); } else if (leafType == LEAF_TYPE_IME_CONTAINERS) { leafArea.mExisting = mImeContainer; } @@ -278,6 +278,17 @@ class DisplayAreaPolicyBuilder { root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas); } + /** Adds all task display areas to the specified layer */ + private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea, int layer) { + final int count = mTaskDisplayAreas.size(); + for (int i = 0; i < count; i++) { + PendingArea leafArea = new PendingArea(null, layer, parentPendingArea); + leafArea.mExisting = mTaskDisplayAreas.get(i); + leafArea.mMaxLayer = layer; + parentPendingArea.mChildren.add(leafArea); + } + } + @Override public void addWindow(WindowToken token) { DisplayArea.Tokens area = findAreaForToken(token); @@ -317,12 +328,16 @@ class DisplayAreaPolicyBuilder { return this; } + protected List<Feature> getFeatures() { + return mFeatures; + } + Result build(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer, - DisplayArea<? extends ActivityStack> taskDisplayArea) { + List<TaskDisplayArea> taskDisplayAreas) { - return new Result(wmService, content, root, imeContainer, taskDisplayArea, new ArrayList<>( + return new Result(wmService, content, root, imeContainer, taskDisplayAreas, new ArrayList<>( mFeatures)); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2816633045b9..40243e8bbedf 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -72,6 +72,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_TASK_OPEN; import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; +import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; @@ -268,8 +269,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo new NonAppWindowContainers("mOverlayContainers", mWmService); /** The containers below are the only child containers {@link #mWindowContainers} can have. */ - // Contains all window containers that are related to apps (Activities) - final TaskDisplayArea mTaskContainers = new TaskDisplayArea(this, mWmService); // Contains all IME window containers. Note that the z-ordering of the IME windows will depend // on the IME target. We mainly have this container grouping so we can keep track of all the IME @@ -971,7 +970,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo super.addChild(mOverlayContainers, null); mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate( - mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers); + mWmService, this, mRootDisplayArea, mImeWindowsContainers); mWindowContainers.addChildren(); // Sets the display content for the children. @@ -2069,13 +2068,11 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } protected int getTaskDisplayAreaCount() { - // TODO(multi-display-area): Report actual display area count - return 1; + return mDisplayAreaPolicy.getTaskDisplayAreaCount(); } protected TaskDisplayArea getTaskDisplayAreaAt(int index) { - // TODO(multi-display-area): Report actual display area values - return mTaskContainers; + return mDisplayAreaPolicy.getTaskDisplayAreaAt(index); } ActivityStack getStack(int rootTaskId) { @@ -2401,7 +2398,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * or for cases when multi-instance is not supported yet (like Split-screen, PiP or Recents). */ TaskDisplayArea getDefaultTaskDisplayArea() { - return mTaskContainers; + return mDisplayAreaPolicy.getTaskDisplayAreaAt(0); } @Override @@ -3424,7 +3421,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private void setInputMethodTarget(WindowState target, boolean targetWaitingAnim) { // Always update control target. This is needed to handle rotation. - updateImeControlTarget(target); + // We cannot set target as the control target, because mInputMethodTarget can only help + // decide the z-order of IME, but cannot control IME. Only the IME target reported from + // updateInputMethodTargetWindow can control IME. + updateImeControlTarget(mInputMethodControlTarget); if (target == mInputMethodTarget && mInputMethodTargetWaitingAnim == targetWaitingAnim) { return; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index ec3c99bf0808..11cfad263fb2 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -31,7 +31,6 @@ import static android.app.WindowConfiguration.isSplitScreenWindowingMode; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.window.DisplayAreaOrganizer.FEATURE_TASK_CONTAINER; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; @@ -141,8 +140,9 @@ final class TaskDisplayArea extends DisplayArea<ActivityStack> { */ private boolean mRemoved; - TaskDisplayArea(DisplayContent displayContent, WindowManagerService service) { - super(service, Type.ANY, "TaskContainers", FEATURE_TASK_CONTAINER); + TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name, + int displayAreaFeature) { + super(service, Type.ANY, name, displayAreaFeature); mDisplayContent = displayContent; mRootWindowContainer = service.mRoot; mAtmService = service.mAtmService; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8d3a4f27a257..513187dc17f5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -32,6 +32,7 @@ import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.content.pm.PackageManager.FEATURE_PC; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import static android.os.Process.myPid; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -2573,6 +2574,8 @@ public class WindowManagerService extends IWindowManager.Stub String packageName, boolean fromClientToken) { final boolean callerCanManageAppTokens = checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()"); + // WindowContext users usually don't hold MANAGE_APP_TOKEN permission. Check permissions + // by checkAddPermission. if (!callerCanManageAppTokens) { final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */, packageName, new int[1]); @@ -2587,7 +2590,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { if (!callerCanManageAppTokens) { if (packageName == null || !unprivilegedAppCanCreateTokenWith( - null /* parentWindow */, callingUid, type, type, null /* tokenForLog */, + null /* parentWindow */, callingUid, type, type, binder, packageName)) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); } @@ -2612,7 +2615,7 @@ public class WindowManagerService extends IWindowManager.Stub new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens); } else { new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens, - false /* roundedCornerOverlay */, fromClientToken); + callingUid, false /* roundedCornerOverlay */, fromClientToken); } } } finally { @@ -2635,8 +2638,25 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void removeWindowToken(IBinder binder, int displayId) { - if (!checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()")) { - throw new SecurityException("Requires MANAGE_APP_TOKENS permission"); + final boolean callerCanManageAppTokens = + checkCallingPermission(MANAGE_APP_TOKENS, "removeWindowToken()"); + final WindowToken windowToken; + synchronized (mGlobalLock) { + windowToken = mRoot.getWindowToken(binder); + } + if (windowToken == null) { + ProtoLog.w(WM_ERROR, + "removeWindowToken: Attempted to remove non-existing token: %s", binder); + return; + } + final int callingUid = Binder.getCallingUid(); + + // If MANAGE_APP_TOKEN permission is not held(usually from WindowContext), callers can only + // remove the window tokens which they added themselves. + if (!callerCanManageAppTokens && (windowToken.getOwnerUid() == INVALID_UID + || callingUid != windowToken.getOwnerUid())) { + throw new SecurityException("removeWindowToken: Requires MANAGE_APP_TOKENS permission" + + " to remove token owned by another uid"); } final long origId = Binder.clearCallingIdentity(); @@ -2649,14 +2669,7 @@ public class WindowManagerService extends IWindowManager.Stub return; } - final WindowToken token = dc.removeWindowToken(binder); - if (token == null) { - ProtoLog.w(WM_ERROR, - "removeWindowToken: Attempted to remove non-existing token: %s", - binder); - return; - } - + dc.removeWindowToken(binder); dc.getInputMonitor().updateInputWindowsLw(true /*force*/); } } finally { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 23091a00a80b..5976b48db764 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.os.Process.INVALID_UID; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -44,6 +45,7 @@ import android.graphics.Rect; import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; +import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.InsetsState; @@ -106,6 +108,11 @@ class WindowToken extends WindowContainer<WindowState> { @VisibleForTesting final boolean mFromClientToken; + private DeathRecipient mDeathRecipient; + private boolean mBinderDied = false; + + private final int mOwnerUid; + /** * Used to fix the transform of the token to be rotated to a rotation different than it's * display. The window frames and surfaces corresponding to this token will be layouted and @@ -165,6 +172,30 @@ class WindowToken extends WindowContainer<WindowState> { } } + private class DeathRecipient implements IBinder.DeathRecipient { + private boolean mHasUnlinkToDeath = false; + + @Override + public void binderDied() { + synchronized (mWmService.mGlobalLock) { + mBinderDied = true; + removeImmediately(); + } + } + + void linkToDeath() throws RemoteException { + token.linkToDeath(DeathRecipient.this, 0); + } + + void unlinkToDeath() { + if (mHasUnlinkToDeath) { + return; + } + token.unlinkToDeath(DeathRecipient.this, 0); + mHasUnlinkToDeath = true; + } + } + /** * Compares two child window of this token and returns -1 if the first is lesser than the * second in terms of z-order and 1 otherwise. @@ -193,23 +224,35 @@ class WindowToken extends WindowContainer<WindowState> { WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay) { - this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, + this(service, _token, type, persistOnEmpty, dc, ownerCanManageAppTokens, INVALID_UID, roundedCornerOverlay, false /* fromClientToken */); } WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty, - DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay, - boolean fromClientToken) { + DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid, + boolean roundedCornerOverlay, boolean fromClientToken) { super(service); token = _token; windowType = type; mPersistOnEmpty = persistOnEmpty; mOwnerCanManageAppTokens = ownerCanManageAppTokens; + mOwnerUid = ownerUid; mRoundedCornerOverlay = roundedCornerOverlay; mFromClientToken = fromClientToken; if (dc != null) { dc.addWindowToken(token, this); } + if (shouldReportToClient()) { + try { + mDeathRecipient = new DeathRecipient(); + mDeathRecipient.linkToDeath(); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to add window token with type " + windowType + " on " + + "display " + dc.getDisplayId(), e); + mDeathRecipient = null; + return; + } + } } void removeAllWindowsIfPossible() { @@ -222,7 +265,7 @@ class WindowToken extends WindowContainer<WindowState> { } void setExiting() { - if (mChildren.size() == 0) { + if (isEmpty()) { super.removeImmediately(); return; } @@ -340,6 +383,21 @@ class WindowToken extends WindowContainer<WindowState> { // Needs to occur after the token is removed from the display above to avoid attempt at // duplicate removal of this window container from it's parent. super.removeImmediately(); + + reportWindowTokenRemovedToClient(); + } + + private void reportWindowTokenRemovedToClient() { + if (!shouldReportToClient()) { + return; + } + mDeathRecipient.unlinkToDeath(); + IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token); + try { + windowTokenClient.onWindowTokenRemoved(); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, "Could not report token removal to the window token client."); + } } @Override @@ -361,17 +419,9 @@ class WindowToken extends WindowContainer<WindowState> { } void reportConfigToWindowTokenClient() { - if (asActivityRecord() != null) { - // Activities are updated through ATM callbacks. + if (!shouldReportToClient()) { return; } - - // Unfortunately, this WindowToken is not from WindowContext so it cannot handle - // its own configuration changes. - if (!mFromClientToken) { - return; - } - final Configuration config = getConfiguration(); final int displayId = getDisplayContent().getDisplayId(); if (config.equals(mLastReportedConfig) && displayId == mLastReportedDisplay) { @@ -383,16 +433,26 @@ class WindowToken extends WindowContainer<WindowState> { mLastReportedDisplay = displayId; IWindowToken windowTokenClient = IWindowToken.Stub.asInterface(token); - if (windowTokenClient != null) { - try { - windowTokenClient.onConfigurationChanged(config, displayId); - } catch (RemoteException e) { - ProtoLog.w(WM_ERROR, - "Could not report config changes to the window token client."); - } + try { + windowTokenClient.onConfigurationChanged(config, displayId); + } catch (RemoteException e) { + ProtoLog.w(WM_ERROR, + "Could not report config changes to the window token client."); } } + /** + * @return {@code true} if this {@link WindowToken} is not an {@link ActivityRecord} and + * registered from client side. + */ + private boolean shouldReportToClient() { + // Only report to client for WindowToken because Activities are updated through ATM + // callbacks. + return asActivityRecord() == null + // Report to {@link android.view.WindowTokenClient} if this token was registered from it. + && mFromClientToken && !mBinderDied; + } + @Override void assignLayer(SurfaceControl.Transaction t, int layer) { if (windowType == TYPE_DOCK_DIVIDER) { @@ -616,4 +676,8 @@ class WindowToken extends WindowContainer<WindowState> { int getWindowLayerFromType() { return mWmService.mPolicy.getWindowLayerFromTypeLw(windowType, mOwnerCanManageAppTokens); } + + int getOwnerUid() { + return mOwnerUid; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 67e83bad154f..d1d353dfbf39 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1836,8 +1836,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Bundle addSyntheticRestrictions(Bundle restrictions) { if (disableCamera) { restrictions.putBoolean(UserManager.DISALLOW_CAMERA, true); - } else { - restrictions.remove(UserManager.DISALLOW_CAMERA); + } + if (requireAutoTime) { + restrictions.putBoolean(UserManager.DISALLOW_CONFIG_DATE_TIME, true); } return restrictions; } @@ -1864,7 +1865,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Bundle getEffectiveRestrictions() { return addSyntheticRestrictions( - removeDeprecatedRestrictions(ensureUserRestrictions())); + removeDeprecatedRestrictions(new Bundle(ensureUserRestrictions()))); } Bundle getLocalUserRestrictions(int adminType) { @@ -2747,6 +2748,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // The following policies weren't available to PO, but will be available after migration. parentAdmin.disableCamera = doAdmin.disableCamera; + parentAdmin.requireAutoTime = doAdmin.requireAutoTime; + // TODO(b/143516163): Uncomment once corresponding APIs are available via parent instance. // parentAdmin.disableScreenCapture = doAdmin.disableScreenCapture; // parentAdmin.accountTypesWithManagementDisabled.addAll( @@ -7839,16 +7842,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final int userHandle = UserHandle.getCallingUserId(); + boolean requireAutoTimeChanged = false; synchronized (getLockObject()) { ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (admin.requireAutoTime != required) { admin.requireAutoTime = required; saveSettingsLocked(userHandle); + requireAutoTimeChanged = true; } } - - // TODO: (b/145604635) Add upgrade case + // requireAutoTime is now backed by DISALLOW_CONFIG_DATE_TIME restriction, so propagate + // updated restrictions to the framework. + if (requireAutoTimeChanged) { + pushUserRestrictions(userHandle); + } // Turn AUTO_TIME on in settings if it is required if (required) { mInjector.binderWithCleanCallingIdentity( diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index d1153e6cf6e7..92366e51eb47 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -51,7 +51,7 @@ using namespace android::content::pm; namespace fs = std::filesystem; constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS"; -constexpr const char* kOpUsage = "android:get_usage_stats"; +constexpr const char* kOpUsage = "android:loader_usage_stats"; namespace android::incremental { diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 0635ae169281..18ae4b5af435 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -137,6 +137,7 @@ public: bool* _aidl_return) { mId = mountId; mListener = listener; + mServiceConnector = control.service; *_aidl_return = true; return binder::Status::ok(); } @@ -166,9 +167,17 @@ public: mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_CREATED); } + int32_t setStorageParams(bool enableReadLogs) { + int32_t result = -1; + EXPECT_NE(mServiceConnector.get(), nullptr); + EXPECT_TRUE(mServiceConnector->setStorageParams(enableReadLogs, &result).isOk()); + return result; + } + private: int mId; sp<IDataLoaderStatusListener> mListener; + sp<IIncrementalServiceConnector> mServiceConnector; sp<IDataLoader> mDataLoader = sp<IDataLoader>(new FakeDataLoader()); }; @@ -453,7 +462,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) { mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); - ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0); + ASSERT_GE(mDataLoaderManager->setStorageParams(true), 0); } TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) { @@ -480,7 +489,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChang mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); - ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0); + ASSERT_GE(mDataLoaderManager->setStorageParams(true), 0); ASSERT_NE(nullptr, mAppOpsManager->mStoredCallback.get()); mAppOpsManager->mStoredCallback->opChanged(0, {}); } @@ -503,7 +512,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) { mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); - ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0); + ASSERT_LT(mDataLoaderManager->setStorageParams(true), 0); } TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) { @@ -526,7 +535,7 @@ TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) { mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); - ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0); + ASSERT_LT(mDataLoaderManager->setStorageParams(true), 0); } TEST_F(IncrementalServiceTest, testMakeDirectory) { diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java index c225d3feb063..1aab6722dfee 100644 --- a/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java +++ b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java @@ -16,6 +16,8 @@ package com.android.server.systemcaptions; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; + import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; @@ -37,6 +39,8 @@ final class RemoteSystemCaptionsManagerService { private static final String SERVICE_INTERFACE = "android.service.systemcaptions.SystemCaptionsManagerService"; + private static final int MSG_BIND = 1; + private final Object mLock = new Object(); private final Context mContext; @@ -71,18 +75,26 @@ final class RemoteSystemCaptionsManagerService { if (mVerbose) { Slog.v(TAG, "initialize()"); } - ensureBound(); + scheduleBind(); + } + + /** + * Destroys this service. + */ + public void destroy() { + mHandler.sendMessage( + obtainMessage(RemoteSystemCaptionsManagerService::handleDestroy, this)); } - void destroy() { + void handleDestroy() { if (mVerbose) { - Slog.v(TAG, "destroy()"); + Slog.v(TAG, "handleDestroy()"); } synchronized (mLock) { if (mDestroyed) { if (mVerbose) { - Slog.v(TAG, "destroy(): Already destroyed"); + Slog.v(TAG, "handleDestroy(): Already destroyed"); } return; } @@ -97,14 +109,24 @@ final class RemoteSystemCaptionsManagerService { } } - private void ensureBound() { + private void scheduleBind() { + if (mHandler.hasMessages(MSG_BIND)) { + if (mVerbose) Slog.v(TAG, "scheduleBind(): already scheduled"); + return; + } + mHandler.sendMessage( + obtainMessage(RemoteSystemCaptionsManagerService::handleEnsureBound, this) + .setWhat(MSG_BIND)); + } + + private void handleEnsureBound() { synchronized (mLock) { if (mService != null || mBinding) { return; } if (mVerbose) { - Slog.v(TAG, "ensureBound(): binding"); + Slog.v(TAG, "handleEnsureBound(): binding"); } mBinding = true; diff --git a/services/tests/servicestests/res/raw/comp_policies_primary.xml b/services/tests/servicestests/res/raw/comp_policies_primary.xml index d30f479195e3..8b7709e0a14f 100644 --- a/services/tests/servicestests/res/raw/comp_policies_primary.xml +++ b/services/tests/servicestests/res/raw/comp_policies_primary.xml @@ -3,6 +3,7 @@ <admin name="com.android.frameworks.servicestests/com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"> <policies flags="991"/> <password-history-length value="33" /> + <require_auto_time value="true" /> <user-restrictions no_bluetooth="true" /> </admin> </policies> diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index de2addffa2c5..c9bd01a31af0 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -385,6 +385,15 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { assertFalse("User restriction was put into non-parent PO instance", dpm.getUserRestrictions(admin1).containsKey(UserManager.DISALLOW_BLUETOOTH)); + assertTrue("User restriction wasn't migrated to PO parent instance", + dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID) + .getParentActiveAdmin() + .getEffectiveRestrictions() + .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME)); + assertFalse("User restriction was put into non-parent PO instance", + dpms.getProfileOwnerAdminLocked(COPE_PROFILE_USER_ID) + .getEffectiveRestrictions() + .containsKey(UserManager.DISALLOW_CONFIG_DATE_TIME)); // TODO(b/143516163): verify more policies. }); } 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 d780370b9849..fe224ce058f4 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -2032,13 +2032,17 @@ public class DevicePolicyManagerTest extends DpmTestBase { eq(false)); DpmTestUtils.assertRestrictions( DpmTestUtils.newRestrictions(UserManager.DISALLOW_CAMERA), - parentDpm.getUserRestrictions(admin1) + dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .getParentActiveAdmin() + .getEffectiveRestrictions() ); parentDpm.setCameraDisabled(admin1, false); DpmTestUtils.assertRestrictions( DpmTestUtils.newRestrictions(), - parentDpm.getUserRestrictions(admin1) + dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .getParentActiveAdmin() + .getEffectiveRestrictions() ); reset(getServices().userManagerInternal); } @@ -2053,7 +2057,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { parentDpm.clearUserRestriction(admin1, restriction); DpmTestUtils.assertRestrictions( DpmTestUtils.newRestrictions(), - parentDpm.getUserRestrictions(admin1) + dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE) + .getParentActiveAdmin() + .getEffectiveRestrictions() ); } @@ -2088,11 +2094,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void assertNoDeviceOwnerRestrictions() { DpmTestUtils.assertRestrictions( DpmTestUtils.newRestrictions(), - getDeviceOwner().ensureUserRestrictions() - ); - DpmTestUtils.assertRestrictions( - DpmTestUtils.newRestrictions(), - dpm.getUserRestrictions(admin1) + getDeviceOwner().getEffectiveRestrictions() ); } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index 12bdec6ec1e1..0568be8d7fa6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; +import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; import static com.android.server.wm.DisplayArea.Type.ANY; @@ -72,7 +73,11 @@ public class DisplayAreaPolicyBuilderTest { WindowManagerService wms = mSystemServices.getWindowManagerService(); DisplayArea.Root root = new SurfacelessDisplayAreaRoot(wms); DisplayArea<WindowContainer> ime = new DisplayArea<>(wms, ABOVE_TASKS, "Ime"); - DisplayArea<ActivityStack> tasks = new DisplayArea<>(wms, ANY, "Tasks"); + DisplayContent displayContent = mock(DisplayContent.class); + TaskDisplayArea taskDisplayArea = new TaskDisplayArea(displayContent, wms, "Tasks", + FEATURE_DEFAULT_TASK_CONTAINER); + List<TaskDisplayArea> taskDisplayAreaList = new ArrayList<>(); + taskDisplayAreaList.add(taskDisplayArea); final Feature foo; final Feature bar; @@ -86,7 +91,7 @@ public class DisplayAreaPolicyBuilderTest { .all() .except(TYPE_STATUS_BAR) .build()) - .build(wms, mock(DisplayContent.class), root, ime, tasks); + .build(wms, displayContent, root, ime, taskDisplayAreaList); policy.attachDisplayAreas(); @@ -98,9 +103,9 @@ public class DisplayAreaPolicyBuilderTest { assertThat(policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)), is(not(decendantOfOneOf(policy.getDisplayAreas(bar))))); - assertThat(tasks, + assertThat(taskDisplayArea, is(decendantOfOneOf(policy.getDisplayAreas(foo)))); - assertThat(tasks, + assertThat(taskDisplayArea, is(decendantOfOneOf(policy.getDisplayAreas(bar)))); assertThat(ime, @@ -109,7 +114,8 @@ public class DisplayAreaPolicyBuilderTest { is(decendantOfOneOf(policy.getDisplayAreas(bar)))); List<DisplayArea<?>> actualOrder = collectLeafAreas(root); - Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, tasks); + Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, root, ime, + taskDisplayArea); actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList()); Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java index 4e4627bf7e52..6834ee5f3950 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java @@ -77,8 +77,7 @@ public class DisplayAreaProviderTest { @Override public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, - DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer, - TaskDisplayArea taskDisplayArea) { + DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer) { throw new RuntimeException("test stub"); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 618e6086b582..880c486c15af 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -54,17 +54,6 @@ public class DisplayAreaTest { } @Test - public void testDisplayArea_positionChanged_throwsIfIncompatibleSibling() { - WindowManagerService wms = mWmsRule.getWindowManagerService(); - DisplayArea<WindowContainer> parent = new SurfacelessDisplayArea<>(wms, ANY, "Parent"); - DisplayArea<WindowContainer> child1 = new DisplayArea<>(wms, ANY, "Child1"); - DisplayArea<WindowContainer> child2 = new DisplayArea<>(wms, ANY, "Child2"); - - parent.addChild(child1, 0); - assertThrows(IllegalStateException.class, () -> parent.addChild(child2, 0)); - } - - @Test public void testType_typeOf() { WindowManagerService wms = mWmsRule.getWindowManagerService(); @@ -87,10 +76,10 @@ public class DisplayAreaTest { checkSiblings(BELOW_TASKS, ABOVE_TASKS); checkSiblings(ANY, ABOVE_TASKS); checkSiblings(ABOVE_TASKS, ABOVE_TASKS); + checkSiblings(ANY, ANY); assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS)); assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY)); - assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, ANY)); assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 673469474709..e47792f4920c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -243,6 +243,26 @@ public class SizeCompatTests extends ActivityTestsBase { } @Test + public void testAspectRatioMatchParentBoundsAndImeAttachable() { + setUpApp(new TestDisplayContent.Builder(mService, 1000, 2000) + .setSystemDecorations(true).build()); + prepareUnresizable(2f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED); + assertFitted(); + + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + mActivity.mDisplayContent.mInputMethodTarget = addWindowToActivity(mActivity); + // Because the aspect ratio of display doesn't exceed the max aspect ratio of activity. + // The activity should still fill its parent container and IME can attach to the activity. + assertTrue(mActivity.matchParentBounds()); + assertTrue(mActivity.mDisplayContent.isImeAttachedToApp()); + + final Rect letterboxInnerBounds = new Rect(); + mActivity.getLetterboxInnerBounds(letterboxInnerBounds); + // The activity should not have letterbox. + assertTrue(letterboxInnerBounds.isEmpty()); + } + + @Test public void testMoveToDifferentOrientDisplay() { setUpDisplaySizeWithApp(1000, 2500); prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 9625ffdac052..a7085334bece 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -51,10 +51,10 @@ import org.junit.Test; import org.junit.runner.RunWith; /** - * Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}. + * Tests for the {@link TaskDisplayArea} container. * * Build/Install/Run: - * atest WmTests:TaskStackContainersTests + * atest WmTests:TaskDisplayAreaTests */ @SmallTest @Presubmit @@ -154,8 +154,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase { ACTIVITY_TYPE_STANDARD, mDisplayContent); final Task newStack = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); - doReturn(newStack).when(mDisplayContent.mTaskContainers).createStack(anyInt(), - anyInt(), anyBoolean(), any(), any(), anyBoolean()); + final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); + doReturn(newStack).when(taskDisplayArea).createStack(anyInt(), anyInt(), anyBoolean(), + any(), any(), anyBoolean()); final int type = ACTIVITY_TYPE_STANDARD; assertGetOrCreateStack(WINDOWING_MODE_FULLSCREEN, type, candidateTask, @@ -186,7 +187,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask, boolean reuseCandidate) { - final TaskDisplayArea taskDisplayArea = (TaskDisplayArea) candidateTask.getParent(); + final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); final ActivityStack stack = taskDisplayArea.getOrCreateStack(windowingMode, activityType, false /* onTop */, null /* intent */, candidateTask /* candidateTask */); assertEquals(reuseCandidate, stack == candidateTask); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java index 18737c2b4bb7..d2a2732d60ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java @@ -43,7 +43,9 @@ class TestDisplayContent extends DisplayContent { // hard-code to FULLSCREEN for tests. setWindowingMode(WINDOWING_MODE_FULLSCREEN); spyOn(this); - spyOn(mTaskContainers); + for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) { + spyOn(getTaskDisplayAreaAt(i)); + } final DisplayRotation displayRotation = getDisplayRotation(); spyOn(displayRotation); @@ -137,6 +139,7 @@ class TestDisplayContent extends DisplayContent { spyOn(displayPolicy); if (mSystemDecorations) { doReturn(true).when(newDisplay).supportsSystemDecorations(); + doReturn(true).when(displayPolicy).hasNavigationBar(); } else { doReturn(false).when(displayPolicy).hasNavigationBar(); doReturn(false).when(displayPolicy).hasStatusBar(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index da4bde59a09e..79b9ae1b902a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -16,13 +16,18 @@ package com.android.server.wm; +import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.content.pm.PackageManager; import android.os.IBinder; @@ -85,4 +90,19 @@ public class WindowManagerServiceTests extends WindowTestsBase { assertFalse(windowToken.mRoundedCornerOverlay); assertTrue(windowToken.mFromClientToken); } + + @Test(expected = SecurityException.class) + public void testRemoveWindowToken_ownerUidNotMatch_throwException() { + IBinder token = mock(IBinder.class); + mWm.addWindowTokenWithOptions(token, TYPE_TOAST, mDisplayContent.getDisplayId(), + null /* options */, null /* options */); + + spyOn(mWm); + when(mWm.checkCallingPermission(anyString(), anyString())).thenReturn(false); + WindowToken windowToken = mWm.mRoot.getWindowToken(token); + spyOn(windowToken); + when(windowToken.getOwnerUid()).thenReturn(INVALID_UID); + + mWm.removeWindowToken(token, mDisplayContent.getDisplayId()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index 7a347cb050be..76479cb95b09 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.os.Process.INVALID_UID; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; @@ -152,7 +153,7 @@ public class WindowTokenTests extends WindowTestsBase { token = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent, true /* ownerCanManageAppTokens */, - true /* roundedCornerOverlay */, true /* fromClientToken */); + INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */); assertTrue(token.mRoundedCornerOverlay); assertTrue(token.mFromClientToken); } @@ -166,7 +167,7 @@ public class WindowTokenTests extends WindowTestsBase { public void testSurfaceCreatedForWindowToken() { final WindowToken fromClientToken = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_APPLICATION_OVERLAY, true /* persistOnEmpty */, - mDisplayContent, true /* ownerCanManageAppTokens */, + mDisplayContent, true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, true /* fromClientToken */); assertNull(fromClientToken.mSurfaceControl); @@ -175,7 +176,7 @@ public class WindowTokenTests extends WindowTestsBase { final WindowToken nonClientToken = new WindowToken(mDisplayContent.mWmService, mock(IBinder.class), TYPE_TOAST, true /* persistOnEmpty */, mDisplayContent, - true /* ownerCanManageAppTokens */, true /* roundedCornerOverlay */, + true /* ownerCanManageAppTokens */, INVALID_UID, true /* roundedCornerOverlay */, false /* fromClientToken */); assertNotNull(nonClientToken.mSurfaceControl); } diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java index d5851d85345f..0c25cfb968fd 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java +++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java @@ -34,18 +34,21 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.server.IoThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.pm.BackgroundDexOptService; import com.android.server.wm.ActivityMetricsLaunchObserver; import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto; import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature; import com.android.server.wm.ActivityMetricsLaunchObserverRegistry; import com.android.server.wm.ActivityTaskManagerInternal; +import java.util.ArrayList; import java.util.concurrent.TimeUnit; import java.util.HashMap; @@ -286,6 +289,7 @@ public class IorapForwardingService extends SystemService { private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver(); private final EventSequenceValidator mEventSequenceValidator = new EventSequenceValidator(); + private final DexOptPackagesUpdated mDexOptPackagesUpdated = new DexOptPackagesUpdated(); private boolean mRegisteredListeners = false; private void registerInProcessListenersLocked() { @@ -308,9 +312,22 @@ public class IorapForwardingService extends SystemService { launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver); launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator); + BackgroundDexOptService.addPackagesUpdatedListener(mDexOptPackagesUpdated); + + mRegisteredListeners = true; } + private class DexOptPackagesUpdated implements BackgroundDexOptService.PackagesUpdatedListener { + @Override + public void onPackagesUpdated(ArraySet<String> updatedPackages) { + String[] updated = updatedPackages.toArray(new String[0]); + for (String packageName : updated) { + Log.d(TAG, "onPackagesUpdated: " + packageName); + } + } + } + private class AppLaunchObserver implements ActivityMetricsLaunchObserver { // We add a synthetic sequence ID here to make it easier to differentiate new // launch sequences on the native side. diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 1a4b9d735977..56f3c3ec0622 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1178,6 +1178,25 @@ public class CarrierConfigManager { "support_ims_conference_call_bool"; /** + * Determines whether the device will locally disconnect an IMS conference when the participant + * count drops to zero. When {@code true}, it is assumed the carrier does NOT disconnect a + * conference when the participant count drops to zero and that the device must do this by + * disconnecting the conference locally. When {@code false}, it is assumed that the carrier + * is responsible for disconnecting the conference when there are no longer any participants + * present. + * <p> + * Note: both {@link #KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL} and + * {@link #KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL} must be true for this configuration to + * have any effect. + * <p> + * Defaults to {@code false}, meaning the carrier network is responsible for disconnecting an + * empty IMS conference. + * @hide + */ + public static final String KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL = + "local_disconnect_empty_ims_conference_bool"; + + /** * Determines whether video conference calls are supported by a carrier. When {@code true}, * video calls can be merged into conference calls, {@code false} otherwiwse. * <p> @@ -3793,6 +3812,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true); + sDefaults.putBoolean(KEY_LOCAL_DISCONNECT_EMPTY_IMS_CONFERENCE_BOOL, false); sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL, true); diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 4c2a984f8198..737665fb97e4 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -17,6 +17,7 @@ package com.android.framework.permission.tests; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import android.os.Binder; import android.os.RemoteException; @@ -27,6 +28,8 @@ import android.view.IWindowManager; import junit.framework.TestCase; +import org.junit.Test; + /** * TODO: Remove this. This is only a placeholder, need to implement this. */ @@ -53,7 +56,7 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.addWindowToken(null, 0, DEFAULT_DISPLAY); + mWm.addWindowToken(null, TYPE_APPLICATION, DEFAULT_DISPLAY); fail("IWindowManager.addWindowToken did not throw SecurityException as" + " expected"); } catch (SecurityException e) { @@ -63,16 +66,6 @@ public class WindowManagerPermissionTests extends TestCase { } try { - mWm.removeWindowToken(null, DEFAULT_DISPLAY); - fail("IWindowManager.removeWindowToken did not throw SecurityException as" - + " expected"); - } catch (SecurityException e) { - // expected - } catch (RemoteException e) { - fail("Unexpected remote exception"); - } - - try { mWm.prepareAppTransition(0, false); fail("IWindowManager.prepareAppTransition did not throw SecurityException as" + " expected"); @@ -182,4 +175,29 @@ public class WindowManagerPermissionTests extends TestCase { fail("Unexpected remote exception"); } } + + @Test + public void testADD_WINDOW_TOKEN_WITH_OPTIONS() { + // Verify if addWindowTokenWithOptions throw SecurityException for privileged window type. + try { + mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, ""); + fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + + // Verify if addWindowTokenWithOptions throw SecurityException for null packageName. + try { + mWm.addWindowTokenWithOptions(null, TYPE_APPLICATION, DEFAULT_DISPLAY, null, null); + fail("IWindowManager.addWindowTokenWithOptions did not throw SecurityException as" + + " expected"); + } catch (SecurityException e) { + // expected + } catch (RemoteException e) { + fail("Unexpected remote exception"); + } + } } diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java index 3026e0b51133..0dd45bad8a01 100644 --- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java +++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java @@ -47,7 +47,8 @@ public final class FrameworksTestsFilter extends SelectTest { "android.view.InsetsSourceConsumerTest", "android.view.InsetsStateTest", "android.view.WindowMetricsTest", - "android.view.PendingInsetsControllerTest" + "android.view.PendingInsetsControllerTest", + "android.app.WindowContextTest" }; public FrameworksTestsFilter(Bundle testArgs) { diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp index 23a0f7278271..5fe94987aa65 100644 --- a/tools/stats_log_api_gen/atoms_info_writer.cpp +++ b/tools/stats_log_api_gen/atoms_info_writer.cpp @@ -42,7 +42,6 @@ static void write_atoms_info_header_body(FILE* out, const Atoms& atoms) { fprintf(out, " const static std::set<int> " "kTruncatingTimestampAtomBlackList;\n"); - fprintf(out, " const static std::map<int, int> kAtomsWithUidField;\n"); fprintf(out, " const static std::set<int> kAtomsWithAttributionChain;\n"); fprintf(out, " const static std::map<int, StateAtomFieldOptions> " @@ -101,28 +100,6 @@ static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) { fprintf(out, "};\n"); fprintf(out, "\n"); - fprintf(out, "static std::map<int, int> getAtomUidField() {\n"); - fprintf(out, " std::map<int, int> uidField;\n"); - for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end(); - atomIt++) { - if ((*atomIt)->uidField == 0) { - continue; - } - fprintf(out, - "\n // Adding uid field for atom " - "(%d)%s\n", - (*atomIt)->code, (*atomIt)->name.c_str()); - fprintf(out, " uidField[%d /* %s */] = %d;\n", (*atomIt)->code, - make_constant_name((*atomIt)->name).c_str(), (*atomIt)->uidField); - } - - fprintf(out, " return uidField;\n"); - fprintf(out, "};\n"); - - fprintf(out, - "const std::map<int, int> AtomsInfo::kAtomsWithUidField = " - "getAtomUidField();\n"); - fprintf(out, "static std::map<int, StateAtomFieldOptions> " "getStateAtomFieldOptions() {\n"); diff --git a/wifi/jarjar-rules.txt b/wifi/jarjar-rules.txt index eeb006ee6ab2..cf11f4347503 100644 --- a/wifi/jarjar-rules.txt +++ b/wifi/jarjar-rules.txt @@ -53,6 +53,9 @@ rule com.google.protobuf.** com.android.server.x.wifi.protobuf.@1 rule com.android.internal.messages.SystemMessageProto* com.android.server.x.wifi.messages.SystemMessageProto@1 # Use our statically linked PlatformProperties library rule android.sysprop.** com.android.server.x.wifi.sysprop.@1 +# Use our statically linked HIDL stubs +rule android.hardware.** com.android.server.x.wifi.hardware.@1 +rule android.hidl.** com.android.server.x.wifi.hidl.@1 # used by both framework-wifi and wifi-service rule android.content.pm.BaseParceledListSlice* android.x.net.wifi.util.BaseParceledListSlice@1 |