diff options
130 files changed, 2393 insertions, 587 deletions
diff --git a/aidl_api/libincremental_aidl/current/android/os/incremental/IncrementalFileSystemControlParcel.aidl b/aidl_api/libincremental_aidl/current/android/os/incremental/IncrementalFileSystemControlParcel.aidl deleted file mode 100644 index d777e34d1240..000000000000 --- a/aidl_api/libincremental_aidl/current/android/os/incremental/IncrementalFileSystemControlParcel.aidl +++ /dev/null @@ -1,24 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // -/////////////////////////////////////////////////////////////////////////////// - -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. -// -// You must not make a backward incompatible changes to the AIDL files built -// with the aidl_interface module type with versions property set. The module -// type is used to build AIDL files in a way that they can be used across -// independently updatable components of the system. If a device is shipped -// with such a backward incompatible change, it has a high risk of breaking -// later when a module using the interface is updated, e.g., Mainline modules. - -package android.os.incremental; -/* @hide */ -parcelable IncrementalFileSystemControlParcel { - ParcelFileDescriptor cmd; - ParcelFileDescriptor pendingReads; - ParcelFileDescriptor log; -} diff --git a/aidl_api/libincremental_manager_aidl/current/android/os/incremental/IIncrementalService.aidl b/aidl_api/libincremental_manager_aidl/current/android/os/incremental/IIncrementalService.aidl deleted file mode 100644 index 5e1f013d627a..000000000000 --- a/aidl_api/libincremental_manager_aidl/current/android/os/incremental/IIncrementalService.aidl +++ /dev/null @@ -1,44 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // -/////////////////////////////////////////////////////////////////////////////// - -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. -// -// You must not make a backward incompatible changes to the AIDL files built -// with the aidl_interface module type with versions property set. The module -// type is used to build AIDL files in a way that they can be used across -// independently updatable components of the system. If a device is shipped -// with such a backward incompatible change, it has a high risk of breaking -// later when a module using the interface is updated, e.g., Mainline modules. - -package android.os.incremental; -/* @hide */ -interface IIncrementalService { - int openStorage(in @utf8InCpp String path); - int createStorage(in @utf8InCpp String path, in android.content.pm.DataLoaderParamsParcel params, in android.content.pm.IDataLoaderStatusListener listener, int createMode); - int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode); - int makeBindMount(int storageId, in @utf8InCpp String sourcePath, in @utf8InCpp String targetFullPath, int bindType); - int deleteBindMount(int storageId, in @utf8InCpp String targetFullPath); - int makeDirectory(int storageId, in @utf8InCpp String path); - int makeDirectories(int storageId, in @utf8InCpp String path); - int makeFile(int storageId, in @utf8InCpp String path, in android.os.incremental.IncrementalNewFileParams params); - int makeFileFromRange(int storageId, in @utf8InCpp String targetPath, in @utf8InCpp String sourcePath, long start, long end); - int makeLink(int sourceStorageId, in @utf8InCpp String sourcePath, int destStorageId, in @utf8InCpp String destPath); - int unlink(int storageId, in @utf8InCpp String path); - boolean isFileRangeLoaded(int storageId, in @utf8InCpp String path, long start, long end); - byte[] getMetadataByPath(int storageId, in @utf8InCpp String path); - byte[] getMetadataById(int storageId, in byte[] fileId); - boolean startLoading(int storageId); - void deleteStorage(int storageId); - boolean configureNativeBinaries(int storageId, in @utf8InCpp String apkFullPath, in @utf8InCpp String libDirRelativePath, in @utf8InCpp String abi); - const int CREATE_MODE_TEMPORARY_BIND = 1; - const int CREATE_MODE_PERMANENT_BIND = 2; - const int CREATE_MODE_CREATE = 4; - const int CREATE_MODE_OPEN_EXISTING = 8; - const int BIND_TEMPORARY = 0; - const int BIND_PERMANENT = 1; -} diff --git a/aidl_api/libincremental_manager_aidl/current/android/os/incremental/IncrementalNewFileParams.aidl b/aidl_api/libincremental_manager_aidl/current/android/os/incremental/IncrementalNewFileParams.aidl deleted file mode 100644 index c73787721113..000000000000 --- a/aidl_api/libincremental_manager_aidl/current/android/os/incremental/IncrementalNewFileParams.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // -/////////////////////////////////////////////////////////////////////////////// - -// This file is a snapshot of an AIDL interface (or parcelable). Do not try to -// edit this file. It looks like you are doing that because you have modified -// an AIDL interface in a backward-incompatible way, e.g., deleting a function -// from an interface or a field from a parcelable and it broke the build. That -// breakage is intended. -// -// You must not make a backward incompatible changes to the AIDL files built -// with the aidl_interface module type with versions property set. The module -// type is used to build AIDL files in a way that they can be used across -// independently updatable components of the system. If a device is shipped -// with such a backward incompatible change, it has a high risk of breaking -// later when a module using the interface is updated, e.g., Mainline modules. - -package android.os.incremental; -/* @hide */ -parcelable IncrementalNewFileParams { - long size; - byte[] fileId; - byte[] metadata; - @nullable byte[] signature; -} diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp index 7480ec834596..6f29141f582c 100644 --- a/apex/statsd/framework/Android.bp +++ b/apex/statsd/framework/Android.bp @@ -146,7 +146,8 @@ java_library { visibility: [ "//frameworks/base", // Framework "//frameworks/base/apex/statsd", // statsd apex - "//frameworks/opt/net/wifi/service" // wifi service + "//frameworks/opt/net/wifi/service", // wifi service + "//packages/providers/MediaProvider", // MediaProvider apk ], } diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java index 62badb41faa8..7fbfc4318949 100644 --- a/apex/statsd/framework/java/android/app/StatsManager.java +++ b/apex/statsd/framework/java/android/app/StatsManager.java @@ -467,10 +467,6 @@ public final class StatsManager { synchronized (sLock) { try { IStatsManagerService service = getIStatsManagerServiceLocked(); - if (service == null) { - throw new StatsUnavailableException("Failed to find statsmanager when " - + "getting experiment IDs"); - } return service.getRegisteredExperimentIds(); } catch (RemoteException e) { if (DEBUG) { diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 776593d5c795..a3214606c32f 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -78,6 +78,7 @@ cc_defaults { "src/matchers/EventMatcherWizard.cpp", "src/matchers/matcher_util.cpp", "src/matchers/SimpleLogMatchingTracker.cpp", + "src/metadata_util.cpp", "src/metrics/CountMetricProducer.cpp", "src/metrics/duration_helper/MaxDurationTracker.cpp", "src/metrics/duration_helper/OringDurationTracker.cpp", @@ -340,6 +341,7 @@ cc_test { "tests/log_event/LogEventQueue_test.cpp", "tests/LogEntryMatcher_test.cpp", "tests/LogEvent_test.cpp", + "tests/metadata_util_test.cpp", "tests/metrics/CountMetricProducer_test.cpp", "tests/metrics/DurationMetricProducer_test.cpp", "tests/metrics/EventMetricProducer_test.cpp", diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 4966b2e1a018..982a63e3e08c 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -79,6 +79,7 @@ constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS #define NS_PER_HOUR 3600 * NS_PER_SEC #define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric" +#define STATS_METADATA_DIR "/data/misc/stats-metadata" // Cool down period for writing data to disk to avoid overwriting files. #define WRITE_DATA_COOL_DOWN_SEC 5 @@ -852,6 +853,110 @@ void StatsLogProcessor::SaveActiveConfigsToDisk(int64_t currentTimeNs) { proto.flush(fd.get()); } +void StatsLogProcessor::SaveMetadataToDisk(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + // Do not write to disk if we already have in the last few seconds. + if (static_cast<unsigned long long> (systemElapsedTimeNs) < + mLastMetadataWriteNs + WRITE_DATA_COOL_DOWN_SEC * NS_PER_SEC) { + ALOGI("Statsd skipping writing metadata to disk. Already wrote data in last %d seconds", + WRITE_DATA_COOL_DOWN_SEC); + return; + } + mLastMetadataWriteNs = systemElapsedTimeNs; + + metadata::StatsMetadataList metadataList; + WriteMetadataToProtoLocked( + currentWallClockTimeNs, systemElapsedTimeNs, &metadataList); + + string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR); + StorageManager::deleteFile(file_name.c_str()); + + if (metadataList.stats_metadata_size() == 0) { + // Skip the write if we have nothing to write. + return; + } + + std::string data; + metadataList.SerializeToString(&data); + StorageManager::writeFile(file_name.c_str(), data.c_str(), data.size()); +} + +void StatsLogProcessor::WriteMetadataToProto(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs, + metadata::StatsMetadataList* metadataList) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + WriteMetadataToProtoLocked(currentWallClockTimeNs, systemElapsedTimeNs, metadataList); +} + +void StatsLogProcessor::WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs, + metadata::StatsMetadataList* metadataList) { + for (const auto& pair : mMetricsManagers) { + const sp<MetricsManager>& metricsManager = pair.second; + metadata::StatsMetadata* statsMetadata = metadataList->add_stats_metadata(); + bool metadataWritten = metricsManager->writeMetadataToProto(currentWallClockTimeNs, + systemElapsedTimeNs, statsMetadata); + if (!metadataWritten) { + metadataList->mutable_stats_metadata()->RemoveLast(); + } + } +} + +void StatsLogProcessor::LoadMetadataFromDisk(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + string file_name = StringPrintf("%s/metadata", STATS_METADATA_DIR); + int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); + if (-1 == fd) { + VLOG("Attempt to read %s but failed", file_name.c_str()); + StorageManager::deleteFile(file_name.c_str()); + return; + } + string content; + if (!android::base::ReadFdToString(fd, &content)) { + ALOGE("Attempt to read %s but failed", file_name.c_str()); + close(fd); + StorageManager::deleteFile(file_name.c_str()); + return; + } + + close(fd); + + metadata::StatsMetadataList statsMetadataList; + if (!statsMetadataList.ParseFromString(content)) { + ALOGE("Attempt to read %s but failed; failed to metadata", file_name.c_str()); + StorageManager::deleteFile(file_name.c_str()); + return; + } + SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs); + StorageManager::deleteFile(file_name.c_str()); +} + +void StatsLogProcessor::SetMetadataState(const metadata::StatsMetadataList& statsMetadataList, + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs) { + std::lock_guard<std::mutex> lock(mMetricsMutex); + SetMetadataStateLocked(statsMetadataList, currentWallClockTimeNs, systemElapsedTimeNs); +} + +void StatsLogProcessor::SetMetadataStateLocked( + const metadata::StatsMetadataList& statsMetadataList, + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs) { + for (const metadata::StatsMetadata& metadata : statsMetadataList.stats_metadata()) { + ConfigKey key(metadata.config_key().uid(), metadata.config_key().config_id()); + auto it = mMetricsManagers.find(key); + if (it == mMetricsManagers.end()) { + ALOGE("No config found for configKey %s", key.ToString().c_str()); + continue; + } + VLOG("Setting metadata %s", key.ToString().c_str()); + it->second->loadMetadata(metadata, currentWallClockTimeNs, systemElapsedTimeNs); + } + VLOG("Successfully loaded %d metadata.", statsMetadataList.stats_metadata_size()); +} + void StatsLogProcessor::WriteActiveConfigsToProtoOutputStream( int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto) { std::lock_guard<std::mutex> lock(mMetricsMutex); diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 42e56760fb89..14585c3df870 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -24,6 +24,7 @@ #include "external/StatsPullerManager.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" #include <stdio.h> #include <unordered_map> @@ -89,6 +90,23 @@ public: /* Load configs containing metrics with active activations from disk. */ void LoadActiveConfigsFromDisk(); + /* Persist metadata for configs and metrics to disk. */ + void SaveMetadataToDisk(int64_t currentWallClockTimeNs, int64_t systemElapsedTimeNs); + + /* Writes the statsd metadata for all configs and metrics to StatsMetadataList. */ + void WriteMetadataToProto(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs, + metadata::StatsMetadataList* metadataList); + + /* Load stats metadata for configs and metrics from disk. */ + void LoadMetadataFromDisk(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs); + + /* Sets the metadata for all configs and metrics */ + void SetMetadataState(const metadata::StatsMetadataList& statsMetadataList, + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs); + /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */ void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs); @@ -173,8 +191,17 @@ private: void SetConfigsActiveStateLocked(const ActiveConfigList& activeConfigList, int64_t currentTimeNs); + void SetMetadataStateLocked(const metadata::StatsMetadataList& statsMetadataList, + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs); + + void WriteMetadataToProtoLocked(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs, + metadata::StatsMetadataList* metadataList); + void WriteDataToDiskLocked(const DumpReportReason dumpReportReason, const DumpLatency dumpLatency); + void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs, const DumpReportReason dumpReportReason, const DumpLatency dumpLatency); @@ -241,6 +268,9 @@ private: // Last time we wrote active metrics to disk. int64_t mLastActiveMetricsWriteNs = 0; + //Last time we wrote metadata to disk. + int64_t mLastMetadataWriteNs = 0; + #ifdef VERY_VERBOSE_PRINTING bool mPrintAllLogs = false; #endif @@ -278,6 +308,9 @@ private: FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); + FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written); + FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk); + FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk); FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 98879a05185d..9169eb1778d9 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1022,6 +1022,7 @@ Status StatsService::informDeviceShutdown() { VLOG("StatsService::informDeviceShutdown"); mProcessor->WriteDataToDisk(DEVICE_SHUTDOWN, FAST); mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs()); + mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs()); return Status::ok(); } @@ -1056,6 +1057,7 @@ Status StatsService::statsCompanionReady() { void StatsService::Startup() { mConfigManager->Startup(); mProcessor->LoadActiveConfigsFromDisk(); + mProcessor->LoadMetadataFromDisk(getWallClockNs(), getElapsedRealtimeNs()); } void StatsService::Terminate() { @@ -1063,6 +1065,7 @@ void StatsService::Terminate() { if (mProcessor != nullptr) { mProcessor->WriteDataToDisk(TERMINATION_SIGNAL_RECEIVED, FAST); mProcessor->SaveActiveConfigsToDisk(getElapsedRealtimeNs()); + mProcessor->SaveMetadataToDisk(getWallClockNs(), getElapsedRealtimeNs()); } } @@ -1295,20 +1298,23 @@ void StatsService::statsCompanionServiceDiedImpl() { if (mProcessor != nullptr) { ALOGW("Reset statsd upon system server restarts."); int64_t systemServerRestartNs = getElapsedRealtimeNs(); - ProtoOutputStream proto; + ProtoOutputStream activeConfigsProto; mProcessor->WriteActiveConfigsToProtoOutputStream(systemServerRestartNs, - STATSCOMPANION_DIED, &proto); - + STATSCOMPANION_DIED, &activeConfigsProto); + metadata::StatsMetadataList metadataList; + mProcessor->WriteMetadataToProto(getWallClockNs(), + systemServerRestartNs, &metadataList); mProcessor->WriteDataToDisk(STATSCOMPANION_DIED, FAST); mProcessor->resetConfigs(); std::string serializedActiveConfigs; - if (proto.serializeToString(&serializedActiveConfigs)) { + if (activeConfigsProto.serializeToString(&serializedActiveConfigs)) { ActiveConfigList activeConfigs; if (activeConfigs.ParseFromString(serializedActiveConfigs)) { mProcessor->SetConfigsActiveState(activeConfigs, systemServerRestartNs); } } + mProcessor->SetMetadataState(metadataList, getWallClockNs(), systemServerRestartNs); } mAnomalyAlarmMonitor->setStatsCompanionService(nullptr); mPeriodicAlarmMonitor->setStatsCompanionService(nullptr); diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index a21abbf042cb..619752c7c44a 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -18,9 +18,11 @@ #include "Log.h" #include "AnomalyTracker.h" -#include "subscriber_util.h" #include "external/Perfetto.h" #include "guardrail/StatsdStats.h" +#include "metadata_util.h" +#include "stats_log_util.h" +#include "subscriber_util.h" #include "subscriber/IncidentdReporter.h" #include "subscriber/SubscriberReporter.h" @@ -262,6 +264,58 @@ void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t me triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions); } +bool AnomalyTracker::writeAlertMetadataToProto(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs, + metadata::AlertMetadata* alertMetadata) { + bool metadataWritten = false; + + if (mRefractoryPeriodEndsSec.empty()) { + return false; + } + + for (const auto& it: mRefractoryPeriodEndsSec) { + // Do not write the timestamp to disk if it has already expired + if (it.second < systemElapsedTimeNs / NS_PER_SEC) { + continue; + } + + metadataWritten = true; + if (alertMetadata->alert_dim_keyed_data_size() == 0) { + alertMetadata->set_alert_id(mAlert.id()); + } + + metadata::AlertDimensionKeyedData* keyedData = alertMetadata->add_alert_dim_keyed_data(); + // We convert and write the refractory_end_sec to wall clock time because we do not know + // when statsd will start again. + int32_t refractoryEndWallClockSec = (int32_t) ((currentWallClockTimeNs / NS_PER_SEC) + + (it.second - systemElapsedTimeNs / NS_PER_SEC)); + + keyedData->set_last_refractory_ends_sec(refractoryEndWallClockSec); + writeMetricDimensionKeyToMetadataDimensionKey( + it.first, keyedData->mutable_dimension_key()); + } + + return metadataWritten; +} + +void AnomalyTracker::loadAlertMetadata( + const metadata::AlertMetadata& alertMetadata, + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs) { + for (const metadata::AlertDimensionKeyedData& keyedData : + alertMetadata.alert_dim_keyed_data()) { + if ((uint64_t) keyedData.last_refractory_ends_sec() < currentWallClockTimeNs / NS_PER_SEC) { + // Do not update the timestamp if it has already expired. + continue; + } + MetricDimensionKey metricKey = loadMetricDimensionKeyFromProto( + keyedData.dimension_key()); + int32_t refractoryPeriodEndsSec = (int32_t) keyedData.last_refractory_ends_sec() - + currentWallClockTimeNs / NS_PER_SEC + systemElapsedTimeNs / NS_PER_SEC; + mRefractoryPeriodEndsSec[metricKey] = refractoryPeriodEndsSec; + } +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index 794ee988ef55..bf36a3bc8990 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -24,6 +24,7 @@ #include "AlarmMonitor.h" #include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert +#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata #include "stats_util.h" // HashableDimensionKey and DimToValMap namespace android { @@ -112,6 +113,17 @@ public: return; // The base AnomalyTracker class doesn't have alarms. } + // Writes metadata of the alert (refractory_period_end_sec) to AlertMetadata. + // Returns true if at least one element is written to alertMetadata. + bool writeAlertMetadataToProto( + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs, metadata::AlertMetadata* alertMetadata); + + void loadAlertMetadata( + const metadata::AlertMetadata& alertMetadata, + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs); + protected: // For testing only. // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 6a4338f7550f..57d4d78cc06d 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -5810,7 +5810,7 @@ message NotificationRemoteViews { */ message PackageNotificationPreferences { // Uid under which the package is installed. - optional int32 uid = 1; + optional int32 uid = 1 [(is_uid) = true]; // Notification importance, which specifies when and how a notification is displayed. // Specified under core/java/android/app/NotificationManager.java. optional int32 importance = 2; @@ -5827,7 +5827,7 @@ message PackageNotificationPreferences { */ message PackageNotificationChannelPreferences { // Uid under which the package is installed. - optional int32 uid = 1; + optional int32 uid = 1 [(is_uid) = true]; // Channel's ID. Should always be available. optional string channel_id = 2; // Channel's name. Should always be available. @@ -5850,7 +5850,7 @@ message PackageNotificationChannelPreferences { */ message PackageNotificationChannelGroupPreferences { // Uid under which the package is installed. - optional int32 uid = 1; + optional int32 uid = 1 [(is_uid) = true]; // Channel Group's ID. Should always be available. optional string group_id = 2; // Channel Group's name. Should always be available. diff --git a/cmds/statsd/src/metadata_util.cpp b/cmds/statsd/src/metadata_util.cpp new file mode 100644 index 000000000000..27ee59b36242 --- /dev/null +++ b/cmds/statsd/src/metadata_util.cpp @@ -0,0 +1,122 @@ +/* + * 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. + */ + +#include "FieldValue.h" +#include "metadata_util.h" + +namespace android { +namespace os { +namespace statsd { + +using google::protobuf::RepeatedPtrField; + +void writeValueToProto(metadata::FieldValue* metadataFieldValue, const Value& value) { + std::string storage_value; + switch (value.getType()) { + case INT: + metadataFieldValue->set_value_int(value.int_value); + break; + case LONG: + metadataFieldValue->set_value_long(value.long_value); + break; + case FLOAT: + metadataFieldValue->set_value_float(value.float_value); + break; + case DOUBLE: + metadataFieldValue->set_value_double(value.double_value); + break; + case STRING: + metadataFieldValue->set_value_str(value.str_value.c_str()); + break; + case STORAGE: // byte array + storage_value = ((char*) value.storage_value.data()); + metadataFieldValue->set_value_storage(storage_value); + break; + default: + break; + } +} + +void writeMetricDimensionKeyToMetadataDimensionKey( + const MetricDimensionKey& metricKey, + metadata::MetricDimensionKey* metadataMetricKey) { + for (const FieldValue& fieldValue : metricKey.getDimensionKeyInWhat().getValues()) { + metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_dimension_key_in_what(); + metadata::Field* metadataField = metadataFieldValue->mutable_field(); + metadataField->set_tag(fieldValue.mField.getTag()); + metadataField->set_field(fieldValue.mField.getField()); + writeValueToProto(metadataFieldValue, fieldValue.mValue); + } + + for (const FieldValue& fieldValue : metricKey.getStateValuesKey().getValues()) { + metadata::FieldValue* metadataFieldValue = metadataMetricKey->add_state_values_key(); + metadata::Field* metadataField = metadataFieldValue->mutable_field(); + metadataField->set_tag(fieldValue.mField.getTag()); + metadataField->set_field(fieldValue.mField.getField()); + writeValueToProto(metadataFieldValue, fieldValue.mValue); + } +} + +void writeFieldValuesFromMetadata( + const RepeatedPtrField<metadata::FieldValue>& repeatedFieldValueList, + std::vector<FieldValue>* fieldValues) { + for (const metadata::FieldValue& metadataFieldValue : repeatedFieldValueList) { + Field field(metadataFieldValue.field().tag(), metadataFieldValue.field().field()); + Value value; + switch (metadataFieldValue.value_case()) { + case metadata::FieldValue::ValueCase::kValueInt: + value = Value(metadataFieldValue.value_int()); + break; + case metadata::FieldValue::ValueCase::kValueLong: + value = Value(metadataFieldValue.value_long()); + break; + case metadata::FieldValue::ValueCase::kValueFloat: + value = Value(metadataFieldValue.value_float()); + break; + case metadata::FieldValue::ValueCase::kValueDouble: + value = Value(metadataFieldValue.value_double()); + break; + case metadata::FieldValue::ValueCase::kValueStr: + value = Value(metadataFieldValue.value_str()); + break; + case metadata::FieldValue::ValueCase::kValueStorage: + value = Value(metadataFieldValue.value_storage()); + break; + default: + break; + } + FieldValue fieldValue(field, value); + fieldValues->emplace_back(field, value); + } +} + +MetricDimensionKey loadMetricDimensionKeyFromProto( + const metadata::MetricDimensionKey& metricDimensionKey) { + std::vector<FieldValue> dimKeyInWhatFieldValues; + writeFieldValuesFromMetadata(metricDimensionKey.dimension_key_in_what(), + &dimKeyInWhatFieldValues); + std::vector<FieldValue> stateValuesFieldValues; + writeFieldValuesFromMetadata(metricDimensionKey.state_values_key(), &stateValuesFieldValues); + + HashableDimensionKey dimKeyInWhat(dimKeyInWhatFieldValues); + HashableDimensionKey stateValues(stateValuesFieldValues); + MetricDimensionKey metricKey(dimKeyInWhat, stateValues); + return metricKey; +} + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/metadata_util.h b/cmds/statsd/src/metadata_util.h new file mode 100644 index 000000000000..84a39ff872b5 --- /dev/null +++ b/cmds/statsd/src/metadata_util.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "HashableDimensionKey.h" + +#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" // AlertMetadata + +namespace android { +namespace os { +namespace statsd { + +void writeMetricDimensionKeyToMetadataDimensionKey(const MetricDimensionKey& metricKey, + metadata::MetricDimensionKey* metadataMetricKey); + +MetricDimensionKey loadMetricDimensionKeyFromProto( + const metadata::MetricDimensionKey& metricDimensionKey); + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 8ed0cbc36779..d832ed86580d 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -642,8 +642,40 @@ void MetricsManager::writeActiveConfigToProtoOutputStream( } } +bool MetricsManager::writeMetadataToProto(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs, + metadata::StatsMetadata* statsMetadata) { + bool metadataWritten = false; + metadata::ConfigKey* configKey = statsMetadata->mutable_config_key(); + configKey->set_config_id(mConfigKey.GetId()); + configKey->set_uid(mConfigKey.GetUid()); + for (const auto& anomalyTracker : mAllAnomalyTrackers) { + metadata::AlertMetadata* alertMetadata = statsMetadata->add_alert_metadata(); + bool alertWritten = anomalyTracker->writeAlertMetadataToProto(currentWallClockTimeNs, + systemElapsedTimeNs, alertMetadata); + if (!alertWritten) { + statsMetadata->mutable_alert_metadata()->RemoveLast(); + } + metadataWritten |= alertWritten; + } + return metadataWritten; +} - +void MetricsManager::loadMetadata(const metadata::StatsMetadata& metadata, + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs) { + for (const metadata::AlertMetadata& alertMetadata : metadata.alert_metadata()) { + int64_t alertId = alertMetadata.alert_id(); + auto it = mAlertTrackerMap.find(alertId); + if (it == mAlertTrackerMap.end()) { + ALOGE("No anomalyTracker found for alertId %lld", (long long) alertId); + continue; + } + mAllAnomalyTrackers[it->second]->loadAlertMetadata(alertMetadata, + currentWallClockTimeNs, + systemElapsedTimeNs); + } +} } // namespace statsd } // namespace os diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 291f97ba03b4..3fb9166bf9bf 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -23,6 +23,7 @@ #include "config/ConfigKey.h" #include "external/StatsPullerManager.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" #include "logd/LogEvent.h" #include "matchers/LogMatchingTracker.h" #include "metrics/MetricProducer.h" @@ -143,6 +144,14 @@ public: void writeActiveConfigToProtoOutputStream( int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto); + // Returns true if at least one piece of metadata is written. + bool writeMetadataToProto(int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs, + metadata::StatsMetadata* statsMetadata); + + void loadMetadata(const metadata::StatsMetadata& metadata, + int64_t currentWallClockTimeNs, + int64_t systemElapsedTimeNs); private: // For test only. inline int64_t getTtlEndNs() const { return mTtlEndNs; } @@ -285,6 +294,9 @@ private: FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket); FRIEND_TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets); + FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written); + FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk); + FRIEND_TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk); FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket); FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets); FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period); diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto index e00fe33655ca..200b392f7542 100644 --- a/cmds/statsd/src/statsd_metadata.proto +++ b/cmds/statsd/src/statsd_metadata.proto @@ -45,11 +45,15 @@ message MetricDimensionKey { repeated FieldValue state_values_key = 2; } +message AlertDimensionKeyedData { + // The earliest time the alert can be fired again in wall clock time. + optional int32 last_refractory_ends_sec = 1; + optional MetricDimensionKey dimension_key = 2; +} + message AlertMetadata { optional int64 alert_id = 1; - // The earliest time the alert can be fired again in wall clock time. - optional int32 last_refractory_ends_sec = 2; - optional MetricDimensionKey dimension_key = 3; + repeated AlertDimensionKeyedData alert_dim_keyed_data = 2; } // All metadata for a config in statsd diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp index 9c6965dc9e6c..c2d70430afdf 100644 --- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp @@ -14,6 +14,7 @@ #include <gtest/gtest.h> +#include "frameworks/base/cmds/statsd/src/statsd_metadata.pb.h" #include "src/StatsLogProcessor.h" #include "src/stats_log_util.h" #include "tests/statsd_test_util.h" @@ -28,7 +29,7 @@ namespace statsd { namespace { -StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { +StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_period_sec) { StatsdConfig config; config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher(); @@ -46,7 +47,7 @@ StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { alert->set_id(StringToId("alert")); alert->set_metric_id(123456); alert->set_num_buckets(num_buckets); - alert->set_refractory_period_secs(10); + alert->set_refractory_period_secs(refractory_period_sec); alert->set_trigger_if_sum_gt(threshold); return config; } @@ -56,9 +57,9 @@ StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) { TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { const int num_buckets = 1; const int threshold = 3; - auto config = CreateStatsdConfig(num_buckets, threshold); + const int refractory_period_sec = 10; + auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); const uint64_t alert_id = config.alert(0).id(); - const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; @@ -173,9 +174,9 @@ TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) { TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { const int num_buckets = 3; const int threshold = 3; - auto config = CreateStatsdConfig(num_buckets, threshold); + const int refractory_period_sec = 10; + auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); const uint64_t alert_id = config.alert(0).id(); - const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000; @@ -240,6 +241,146 @@ TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) { anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); } +TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) { + const int num_buckets = 1; + const int threshold = 0; + const int refractory_period_sec = 86400 * 365; // 1 year + auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); + const int64_t alert_id = config.alert(0).id(); + + int64_t bucketStartTimeNs = 10000000000; + + int configUid = 2000; + int64_t configId = 1000; + ConfigKey cfgKey(configUid, configId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + metadata::StatsMetadataList result; + int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; + int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; + processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result); + + EXPECT_EQ(result.stats_metadata_size(), 0); +} + +TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk) { + const int num_buckets = 1; + const int threshold = 0; + const int refractory_period_sec = 86400 * 365; // 1 year + auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); + const int64_t alert_id = config.alert(0).id(); + + int64_t bucketStartTimeNs = 10000000000; + + int configUid = 2000; + int64_t configId = 1000; + ConfigKey cfgKey(configUid, configId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + std::vector<int> attributionUids1 = {111}; + std::vector<string> attributionTags1 = {"App1"}; + std::vector<int> attributionUids2 = {111, 222}; + std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"}; + + FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)111)); + HashableDimensionKey whatKey1({fieldValue1}); + MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + metadata::StatsMetadataList result; + int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; + int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; + processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result); + + metadata::StatsMetadata statsMetadata = result.stats_metadata(0); + EXPECT_EQ(result.stats_metadata_size(), 1); + EXPECT_EQ(statsMetadata.config_key().config_id(), configId); + EXPECT_EQ(statsMetadata.config_key().uid(), configUid); + + metadata::AlertMetadata alertMetadata = statsMetadata.alert_metadata(0); + EXPECT_EQ(statsMetadata.alert_metadata_size(), 1); + EXPECT_EQ(alertMetadata.alert_id(), alert_id); + metadata::AlertDimensionKeyedData keyedData = alertMetadata.alert_dim_keyed_data(0); + EXPECT_EQ(alertMetadata.alert_dim_keyed_data_size(), 1); + EXPECT_EQ(keyedData.last_refractory_ends_sec(), + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) - + mockElapsedTimeNs / NS_PER_SEC + + mockWallClockNs / NS_PER_SEC); + + metadata::MetricDimensionKey metadataDimKey = keyedData.dimension_key(); + metadata::FieldValue dimKeyInWhat = metadataDimKey.dimension_key_in_what(0); + EXPECT_EQ(dimKeyInWhat.field().tag(), fieldValue1.mField.getTag()); + EXPECT_EQ(dimKeyInWhat.field().field(), fieldValue1.mField.getField()); + EXPECT_EQ(dimKeyInWhat.value_int(), fieldValue1.mValue.int_value); +} + +TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk) { + const int num_buckets = 1; + const int threshold = 0; + const int refractory_period_sec = 86400 * 365; // 1 year + auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec); + const int64_t alert_id = config.alert(0).id(); + + int64_t bucketStartTimeNs = 10000000000; + + int configUid = 2000; + int64_t configId = 1000; + ConfigKey cfgKey(configUid, configId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); + + sp<AnomalyTracker> anomalyTracker = + processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + + std::vector<int> attributionUids1 = {111}; + std::vector<string> attributionTags1 = {"App1"}; + std::vector<int> attributionUids2 = {111, 222}; + std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"}; + + FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101), + Value((int32_t)111)); + HashableDimensionKey whatKey1({fieldValue1}); + MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY); + + auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1, + attributionTags1, "wl1"); + processor->OnLogEvent(event.get()); + EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); + + int64_t mockWallClockNs = 1584991200 * NS_PER_SEC; + int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC; + processor->SaveMetadataToDisk(mockWallClockNs, mockElapsedTimeNs); + + auto processor2 = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + int64_t mockElapsedTimeSinceBoot = 10 * NS_PER_SEC; + processor2->LoadMetadataFromDisk(mockWallClockNs, mockElapsedTimeSinceBoot); + + sp<AnomalyTracker> anomalyTracker2 = + processor2->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; + EXPECT_EQ(anomalyTracker2->getRefractoryPeriodEndsSec(dimensionKey1) - + mockElapsedTimeSinceBoot / NS_PER_SEC, + anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) - + mockElapsedTimeNs / NS_PER_SEC); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/metadata_util_test.cpp b/cmds/statsd/tests/metadata_util_test.cpp new file mode 100644 index 000000000000..7707890cbd0c --- /dev/null +++ b/cmds/statsd/tests/metadata_util_test.cpp @@ -0,0 +1,69 @@ +/* + * 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. + */ +#include <gtest/gtest.h> + +#include "metadata_util.h" +#include "tests/statsd_test_util.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +TEST(MetadataUtilTest, TestWriteAndReadMetricDimensionKey) { + HashableDimensionKey dim; + HashableDimensionKey dim2; + int pos1[] = {1, 1, 1}; + int pos2[] = {1, 1, 2}; + int pos3[] = {1, 1, 3}; + int pos4[] = {2, 0, 0}; + Field field1(10, pos1, 2); + Field field2(10, pos2, 2); + Field field3(10, pos3, 2); + Field field4(10, pos4, 0); + + Value value1((int32_t)10025); + Value value2("tag"); + Value value3((int32_t)987654); + Value value4((int32_t)99999); + + dim.addValue(FieldValue(field1, value1)); + dim.addValue(FieldValue(field2, value2)); + dim.addValue(FieldValue(field3, value3)); + dim.addValue(FieldValue(field4, value4)); + + dim2.addValue(FieldValue(field1, value1)); + dim2.addValue(FieldValue(field2, value2)); + + MetricDimensionKey dimKey(dim, dim2); + + metadata::MetricDimensionKey metadataDimKey; + writeMetricDimensionKeyToMetadataDimensionKey(dimKey, &metadataDimKey); + + MetricDimensionKey loadedDimKey = loadMetricDimensionKeyFromProto(metadataDimKey); + + ASSERT_EQ(loadedDimKey, dimKey); + ASSERT_EQ(std::hash<MetricDimensionKey>{}(loadedDimKey), + std::hash<MetricDimensionKey>{}(dimKey)); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 9b42fa2012e1..f461a1708373 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1232,6 +1232,10 @@ public class Notification implements Parcelable /** @hide */ public static final String EXTRA_CONVERSATION_ICON = "android.conversationIcon"; + /** @hide */ + public static final String EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT = + "android.conversationUnreadMessageCount"; + /** * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message} * bundles provided by a @@ -7102,6 +7106,7 @@ public class Notification implements Parcelable List<Message> mHistoricMessages = new ArrayList<>(); boolean mIsGroupConversation; @ConversationType int mConversationType = CONVERSATION_TYPE_LEGACY; + int mUnreadMessageCount; MessagingStyle() { } @@ -7247,6 +7252,17 @@ public class Notification implements Parcelable return mConversationType; } + /** @hide */ + public int getUnreadMessageCount() { + return mUnreadMessageCount; + } + + /** @hide */ + public MessagingStyle setUnreadMessageCount(int unreadMessageCount) { + mUnreadMessageCount = unreadMessageCount; + return this; + } + /** * Adds a message for display by this notification. Convenience call for a simple * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}. @@ -7401,6 +7417,7 @@ public class Notification implements Parcelable if (mShortcutIcon != null) { extras.putParcelable(EXTRA_CONVERSATION_ICON, mShortcutIcon); } + extras.putInt(EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT, mUnreadMessageCount); fixTitleAndTextExtras(extras); extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, mIsGroupConversation); @@ -7452,6 +7469,7 @@ public class Notification implements Parcelable Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES); mHistoricMessages = Message.getMessagesFromBundleArray(histMessages); mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION); + mUnreadMessageCount = extras.getInt(EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT); } /** @@ -7601,6 +7619,7 @@ public class Notification implements Parcelable : mBuilder.getMessagingLayoutResource(), p, bindResult); + addExtras(mBuilder.mN.extras); if (!isConversationLayout) { // also update the end margin if there is an image diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 435c70ae999b..eee91ce173dc 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -109,4 +109,8 @@ public abstract class ShortcutServiceInternal { */ public abstract String getShortcutIconUri(int launcherUserId, @NonNull String launcherPackage, @NonNull String packageName, @NonNull String shortcutId, int userId); + + public abstract boolean isSharingShortcut(int callingUserId, @NonNull String callingPackage, + @NonNull String packageName, @NonNull String shortcutId, int userId, + @NonNull IntentFilter filter); } diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl index 2dbaea860e2a..d8308c7c3362 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -38,6 +38,13 @@ interface IIncrementalService { int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode); /** + * Changes storage params. Returns 0 on success, and -errno on failure. + * Use enableReadLogs to switch pages read logs reporting on and off. + * Returns 0 on success, and - errno on failure: permission check or remount. + */ + int setStorageParams(int storageId, boolean enableReadLogs); + + /** * Bind-mounts a path under a storage to a full path. Can be permanent or temporary. */ const int BIND_TEMPORARY = 0; diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 35518db32829..5f01408944e8 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -19,11 +19,13 @@ package android.os.incremental; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; import android.content.pm.DataLoaderParams; import android.content.pm.IDataLoaderStatusListener; import android.os.RemoteException; +import android.system.ErrnoException; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -319,6 +321,23 @@ public final class IncrementalManager { return nativeUnsafeGetFileSignature(path); } + /** + * Sets storage parameters. + * + * @param enableReadLogs - enables or disables read logs. Caller has to have a permission. + */ + @RequiresPermission(android.Manifest.permission.LOADER_USAGE_STATS) + public void setStorageParams(int storageId, boolean enableReadLogs) throws ErrnoException { + try { + int res = mService.setStorageParams(storageId, enableReadLogs); + if (res < 0) { + throw new ErrnoException("setStorageParams", -res); + } + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + /* Native methods */ private static native boolean nativeIsEnabled(); private static native boolean nativeIsIncrementalPath(@NonNull String path); diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java index c047dc0d07c7..05877a59368a 100644 --- a/core/java/android/service/dataloader/DataLoaderService.java +++ b/core/java/android/service/dataloader/DataLoaderService.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.content.pm.DataLoaderParams; import android.content.pm.DataLoaderParamsParcel; @@ -31,6 +32,8 @@ import android.content.pm.InstallationFile; import android.content.pm.InstallationFileParcel; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.incremental.IncrementalManager; +import android.system.ErrnoException; import android.util.ExceptionUtils; import android.util.Slog; @@ -208,6 +211,25 @@ public abstract class DataLoaderService extends Service { private final long mNativeInstance; } + /* Used by native FileSystemConnector. */ + private boolean setStorageParams(int storageId, boolean enableReadLogs) { + IncrementalManager incrementalManager = (IncrementalManager) getSystemService( + Context.INCREMENTAL_SERVICE); + if (incrementalManager == null) { + Slog.e(TAG, "Failed to obtain incrementalManager: " + storageId); + return false; + } + try { + // This has to be done directly in incrementalManager as the storage + // might be missing still. + incrementalManager.setStorageParams(storageId, enableReadLogs); + } catch (ErrnoException e) { + Slog.e(TAG, "Failed to set params for storage: " + storageId, e); + return false; + } + return true; + } + /* Native methods */ private native boolean nativeCreateDataLoader(int storageId, @NonNull FileSystemControlParcel control, diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java index 35286ba007c4..2461e96c5b49 100644 --- a/core/java/android/view/ImeInsetsSourceConsumer.java +++ b/core/java/android/view/ImeInsetsSourceConsumer.java @@ -16,7 +16,6 @@ package android.view; -import static android.view.InsetsController.ANIMATION_TYPE_USER; import static android.view.InsetsController.AnimationType; import static android.view.InsetsState.ITYPE_IME; @@ -104,13 +103,9 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer { void hide(boolean animationFinished, @AnimationType int animationType) { super.hide(); - if (!animationFinished) { - if (animationType == ANIMATION_TYPE_USER) { - // if controlWindowInsetsAnimation is hiding keyboard. - notifyHidden(); - } - } else { + if (animationFinished) { // remove IME surface as IME has finished hide animation. + notifyHidden(); removeSurface(); } } diff --git a/core/java/android/view/InsetsAnimationControlCallbacks.java b/core/java/android/view/InsetsAnimationControlCallbacks.java index 4227f78564a7..74c186948b2f 100644 --- a/core/java/android/view/InsetsAnimationControlCallbacks.java +++ b/core/java/android/view/InsetsAnimationControlCallbacks.java @@ -40,8 +40,10 @@ public interface InsetsAnimationControlCallbacks { /** * Schedule the apply by posting the animation callback. + * + * @param runner The runner that requested applying insets */ - void scheduleApplyChangeInsets(); + void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner); /** * Finish the final steps after the animation. diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index ef6a9b212e92..05abc6032116 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -156,7 +156,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll mPendingFraction = sanitize(fraction); mPendingInsets = sanitize(insets); mPendingAlpha = sanitize(alpha); - mController.scheduleApplyChangeInsets(); + mController.scheduleApplyChangeInsets(this); } @VisibleForTesting diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index 40ffa7ef48bf..9dfdd0604e6b 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -54,7 +54,7 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro } @Override - public void scheduleApplyChangeInsets() { + public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { mControl.applyChangeInsets(mState); } @@ -91,7 +91,7 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro @AnimationType int animationType, Handler mainThreadHandler) { mMainThreadHandler = mainThreadHandler; mOuterCallbacks = controller; - mControl = new InsetsAnimationControlImpl(copyControls(controls), frame, state, listener, + mControl = new InsetsAnimationControlImpl(controls, frame, state, listener, types, mCallbacks, durationMs, interpolator, animationType); InsetsAnimationThread.getHandler().post(() -> listener.onReady(mControl, types)); } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 8eb9b5f6ef23..72ddaca27a76 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1076,8 +1076,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @Override - public void scheduleApplyChangeInsets() { - if (mStartingAnimation) { + public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { + if (mStartingAnimation || runner.getAnimationType() == ANIMATION_TYPE_USER) { mAnimCallback.run(); mAnimCallbackScheduled = false; return; diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index 83ff8fa42f49..f74221dc3e3a 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -117,7 +117,7 @@ public class InsetsSourceConsumer { } } if (lastControl != null) { - lastControl.release(mController::releaseSurfaceControlFromRt); + lastControl.release(SurfaceControl::release); } } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index c5154662928e..9896aa4fe214 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -172,6 +172,10 @@ public class InsetsState implements Parcelable { for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { InsetsSource source = mSources.get(type); if (source == null) { + int index = indexOf(toPublicType(type)); + if (typeInsetsMap[index] == null) { + typeInsetsMap[index] = Insets.NONE; + } continue; } diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 0359f3b4fde7..a9f3e04036b5 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -60,11 +60,7 @@ public class NotificationHeaderView extends ViewGroup { private NotificationExpandButton mExpandButton; private CachingIconView mIcon; private View mProfileBadge; - private View mOverlayIcon; - private View mCameraIcon; - private View mMicIcon; private View mAppOps; - private View mAudiblyAlertedIcon; private boolean mExpanded; private boolean mShowExpandButtonAtEnd; private boolean mShowWorkBadgeAtEnd; @@ -121,11 +117,7 @@ public class NotificationHeaderView extends ViewGroup { mExpandButton = findViewById(com.android.internal.R.id.expand_button); mIcon = findViewById(com.android.internal.R.id.icon); mProfileBadge = findViewById(com.android.internal.R.id.profile_badge); - mCameraIcon = findViewById(com.android.internal.R.id.camera); - mMicIcon = findViewById(com.android.internal.R.id.mic); - mOverlayIcon = findViewById(com.android.internal.R.id.overlay); mAppOps = findViewById(com.android.internal.R.id.app_ops); - mAudiblyAlertedIcon = findViewById(com.android.internal.R.id.alerted_icon); } @Override @@ -300,10 +292,6 @@ public class NotificationHeaderView extends ViewGroup { */ public void setAppOpsOnClickListener(OnClickListener l) { mAppOpsListener = l; - mAppOps.setOnClickListener(mAppOpsListener); - mCameraIcon.setOnClickListener(mAppOpsListener); - mMicIcon.setOnClickListener(mAppOpsListener); - mOverlayIcon.setOnClickListener(mAppOpsListener); updateTouchListener(); } @@ -328,27 +316,6 @@ public class NotificationHeaderView extends ViewGroup { updateExpandButton(); } - /** - * Shows or hides 'app op in use' icons based on app usage. - */ - public void showAppOpsIcons(ArraySet<Integer> appOps) { - if (mOverlayIcon == null || mCameraIcon == null || mMicIcon == null || appOps == null) { - return; - } - - mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) - ? View.VISIBLE : View.GONE); - mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA) - ? View.VISIBLE : View.GONE); - mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO) - ? View.VISIBLE : View.GONE); - } - - /** Updates icon visibility based on the noisiness of the notification. */ - public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) { - mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE); - } - private void updateExpandButton() { int drawableId; int contentDescriptionId; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 51304dcfe8cb..35f955f7e78b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -130,6 +130,7 @@ import android.view.View.MeasureSpec; import android.view.Window.OnContentApplyWindowInsetsListener; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; +import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -1412,6 +1413,10 @@ public final class ViewRootImpl implements ViewParent, | (oldSoftInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST); } + if ((changes & LayoutParams.SOFT_INPUT_MODE_CHANGED) != 0) { + requestFitSystemWindows(); + } + mWindowAttributesChanged = true; scheduleTraversals(); } diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 9b2a6cbce48f..ca3dd04fd756 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -818,12 +818,13 @@ public final class WindowInsets { * @return A modified copy of this WindowInsets * @deprecated Consuming of different parts individually of a {@link WindowInsets} instance is * deprecated, since {@link WindowInsets} contains many different insets. Use {@link #CONSUMED} - * instead to stop dispatching insets. + * instead to stop dispatching insets. On {@link android.os.Build.VERSION_CODES#R R}, this + * method has no effect. */ @Deprecated @NonNull public WindowInsets consumeStableInsets() { - return consumeSystemWindowInsets(); + return this; } /** @@ -835,12 +836,24 @@ public final class WindowInsets { @Override public String toString() { - return "WindowInsets{systemWindowInsets=" + getSystemWindowInsets() - + " stableInsets=" + getStableInsets() - + " sysGestureInsets=" + getSystemGestureInsets() - + (mDisplayCutout != null ? " cutout=" + mDisplayCutout : "") - + (isRound() ? " round" : "") - + "}"; + StringBuilder result = new StringBuilder("WindowInsets{\n "); + for (int i = 0; i < SIZE; i++) { + Insets insets = mTypeInsetsMap[i]; + Insets maxInsets = mTypeMaxInsetsMap[i]; + boolean visible = mTypeVisibilityMap[i]; + if (!Insets.NONE.equals(insets) || !Insets.NONE.equals(maxInsets) || visible) { + result.append(Type.toString(1 << i)).append("=").append(insets) + .append(" max=").append(maxInsets) + .append(" vis=").append(visible) + .append("\n "); + } + } + + result.append(mDisplayCutout != null ? "cutout=" + mDisplayCutout : ""); + result.append("\n "); + result.append(isRound() ? "round" : ""); + result.append("}"); + return result.toString(); } /** @@ -1309,6 +1322,32 @@ public final class WindowInsets { } } + static String toString(@InsetsType int type) { + switch (type) { + case STATUS_BARS: + return "statusBars"; + case NAVIGATION_BARS: + return "navigationBars"; + case CAPTION_BAR: + return "captionBar"; + case IME: + return "ime"; + case SYSTEM_GESTURES: + return "systemGestures"; + case MANDATORY_SYSTEM_GESTURES: + return "mandatorySystemGestures"; + case TAPPABLE_ELEMENT: + return "tappableElement"; + case DISPLAY_CUTOUT: + return "displayCutout"; + case WINDOW_DECOR: + return "windowDecor"; + default: + throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST," + + " type=" + type); + } + } + private Type() { } diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java index 6aa288ddc638..af896fca932a 100644 --- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java +++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java @@ -30,6 +30,7 @@ import android.widget.inline.InlinePresentationSpec; import com.android.internal.util.DataClass; import com.android.internal.util.Preconditions; +import com.android.internal.widget.InlinePresentationStyleUtils; import java.util.ArrayList; import java.util.List; @@ -113,6 +114,10 @@ public final class InlineSuggestionsRequest implements Parcelable { mHostInputToken = hostInputToken; } + private boolean extrasEquals(@NonNull Bundle extras) { + return InlinePresentationStyleUtils.bundleEquals(mExtras, extras); + } + // TODO(b/149609075): remove once IBinder parcelling is natively supported private void parcelHostInputToken(@NonNull Parcel parcel, int flags) { parcel.writeStrongBinder(mHostInputToken); @@ -331,7 +336,7 @@ public final class InlineSuggestionsRequest implements Parcelable { && java.util.Objects.equals(mInlinePresentationSpecs, that.mInlinePresentationSpecs) && java.util.Objects.equals(mHostPackageName, that.mHostPackageName) && java.util.Objects.equals(mSupportedLocales, that.mSupportedLocales) - && java.util.Objects.equals(mExtras, that.mExtras) + && extrasEquals(that.mExtras) && java.util.Objects.equals(mHostInputToken, that.mHostInputToken) && mHostDisplayId == that.mHostDisplayId; } @@ -603,10 +608,10 @@ public final class InlineSuggestionsRequest implements Parcelable { } @DataClass.Generated( - time = 1585691147541L, + time = 1585768018462L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java", - inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs()\npublic void setHostInputToken(android.os.IBinder)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(android.view.inline.InlinePresentationSpec)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> getPresentationSpecs()\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\npublic @android.compat.annotation.UnsupportedAppUsage @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsRequest.Builder addPresentationSpecs(android.view.inline.InlinePresentationSpec)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/widget/inline/InlinePresentationSpec.java b/core/java/android/widget/inline/InlinePresentationSpec.java index 00eb3ce271a1..5635857154de 100644 --- a/core/java/android/widget/inline/InlinePresentationSpec.java +++ b/core/java/android/widget/inline/InlinePresentationSpec.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import android.util.Size; import com.android.internal.util.DataClass; +import com.android.internal.widget.InlinePresentationStyleUtils; /** * This class represents the presentation specification by which an inline suggestion @@ -52,6 +53,10 @@ public final class InlinePresentationSpec implements Parcelable { return Bundle.EMPTY; } + private boolean styleEquals(@NonNull Bundle style) { + return InlinePresentationStyleUtils.bundleEquals(mStyle, style); + } + /** @hide */ @DataClass.Suppress({"setMaxSize", "setMinSize"}) abstract static class BaseBuilder { @@ -143,7 +148,7 @@ public final class InlinePresentationSpec implements Parcelable { return true && java.util.Objects.equals(mMinSize, that.mMinSize) && java.util.Objects.equals(mMaxSize, that.mMaxSize) - && java.util.Objects.equals(mStyle, that.mStyle); + && styleEquals(that.mStyle); } @Override @@ -280,10 +285,10 @@ public final class InlinePresentationSpec implements Parcelable { } @DataClass.Generated( - time = 1585605466300L, + time = 1585768046898L, codegenVersion = "1.0.15", sourceFile = "frameworks/base/core/java/android/widget/inline/InlinePresentationSpec.java", - inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static @android.annotation.NonNull android.os.Bundle defaultStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []") + inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.NonNull android.os.Bundle mStyle\nprivate static @android.annotation.NonNull android.os.Bundle defaultStyle()\nprivate boolean styleEquals(android.os.Bundle)\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java index 0b2dcd8b3a92..1afbfeb96fc3 100644 --- a/core/java/android/window/VirtualDisplayTaskEmbedder.java +++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java @@ -248,7 +248,6 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { options = super.prepareActivityOptions(options); options.setLaunchDisplayId(getDisplayId()); options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - options.setTaskAlwaysOnTop(true); return options; } diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index 1336ec412cdb..ab68c440483e 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -45,6 +45,7 @@ import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.RemotableViewMethod; +import android.view.TouchDelegate; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -62,6 +63,7 @@ import com.android.internal.util.ContrastColorUtil; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -151,6 +153,10 @@ public class ConversationLayout extends FrameLayout private int mFacePileProtectionWidth; private int mFacePileProtectionWidthExpanded; private boolean mImportantConversation; + private TextView mUnreadBadge; + private ViewGroup mAppOps; + private Rect mAppOpsTouchRect = new Rect(); + private float mMinTouchSize; public ConversationLayout(@NonNull Context context) { super(context); @@ -189,6 +195,8 @@ public class ConversationLayout extends FrameLayout mConversationIcon = findViewById(R.id.conversation_icon); mConversationIconContainer = findViewById(R.id.conversation_icon_container); mIcon = findViewById(R.id.icon); + mAppOps = findViewById(com.android.internal.R.id.app_ops); + mMinTouchSize = 48 * getResources().getDisplayMetrics().density; mImportanceRingView = findViewById(R.id.conversation_icon_badge_ring); mConversationIconBadge = findViewById(R.id.conversation_icon_badge); mConversationIconBadgeBg = findViewById(R.id.conversation_icon_badge_bg); @@ -277,6 +285,7 @@ public class ConversationLayout extends FrameLayout mAppName.setOnVisibilityChangedListener((visibility) -> { onAppNameVisibilityChanged(); }); + mUnreadBadge = findViewById(R.id.conversation_unread_count); mConversationContentStart = getResources().getDimensionPixelSize( R.dimen.conversation_content_start); mInternalButtonPadding @@ -354,7 +363,6 @@ public class ConversationLayout extends FrameLayout // mUser now set (would be nice to avoid the side effect but WHATEVER) setUser(extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON)); - // Append remote input history to newMessages (again, side effect is lame but WHATEVS) RemoteInputHistoryItem[] history = (RemoteInputHistoryItem[]) extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS); @@ -362,9 +370,11 @@ public class ConversationLayout extends FrameLayout boolean showSpinner = extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); - // bind it, baby bind(newMessages, newHistoricMessages, showSpinner); + + int unreadCount = extras.getInt(Notification.EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT); + setUnreadCount(unreadCount); } @Override @@ -372,6 +382,18 @@ public class ConversationLayout extends FrameLayout mImageResolver = resolver; } + /** @hide */ + public void setUnreadCount(int unreadCount) { + mUnreadBadge.setVisibility(mIsCollapsed && unreadCount > 1 ? VISIBLE : GONE); + CharSequence text = unreadCount >= 100 + ? getResources().getString(R.string.unread_convo_overflow, 99) + : String.format(Locale.getDefault(), "%d", unreadCount); + mUnreadBadge.setText(text); + mUnreadBadge.setBackgroundTintList(ColorStateList.valueOf(mLayoutColor)); + boolean needDarkText = ColorUtils.calculateLuminance(mLayoutColor) > 0.5f; + mUnreadBadge.setTextColor(needDarkText ? Color.BLACK : Color.WHITE); + } + private void addRemoteInputHistoryToMessages( List<Notification.MessagingStyle.Message> newMessages, RemoteInputHistoryItem[] remoteInputHistory) { @@ -855,6 +877,7 @@ public class ConversationLayout extends FrameLayout @RemotableViewMethod public void setSenderTextColor(int color) { mSenderTextColor = color; + mConversationText.setTextColor(color); } /** @@ -1055,6 +1078,47 @@ public class ConversationLayout extends FrameLayout } }); } + if (mAppOps.getWidth() > 0) { + + // Let's increase the touch size of the app ops view if it's here + mAppOpsTouchRect.set( + mAppOps.getLeft(), + mAppOps.getTop(), + mAppOps.getRight(), + mAppOps.getBottom()); + for (int i = 0; i < mAppOps.getChildCount(); i++) { + View child = mAppOps.getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + // Make sure each child has at least a minTouchSize touch target around it + float childTouchLeft = child.getLeft() + child.getWidth() / 2.0f + - mMinTouchSize / 2.0f; + float childTouchRight = childTouchLeft + mMinTouchSize; + mAppOpsTouchRect.left = (int) Math.min(mAppOpsTouchRect.left, + mAppOps.getLeft() + childTouchLeft); + mAppOpsTouchRect.right = (int) Math.max(mAppOpsTouchRect.right, + mAppOps.getLeft() + childTouchRight); + } + + // Increase the height + int heightIncrease = 0; + if (mAppOpsTouchRect.height() < mMinTouchSize) { + heightIncrease = (int) Math.ceil((mMinTouchSize - mAppOpsTouchRect.height()) + / 2.0f); + } + mAppOpsTouchRect.inset(0, -heightIncrease); + + // Let's adjust the hitrect since app ops isn't a direct child + ViewGroup viewGroup = (ViewGroup) mAppOps.getParent(); + while (viewGroup != this) { + mAppOpsTouchRect.offset(viewGroup.getLeft(), viewGroup.getTop()); + viewGroup = (ViewGroup) viewGroup.getParent(); + } + // + // Extend the size of the app opps to be at least 48dp + setTouchDelegate(new TouchDelegate(mAppOpsTouchRect, mAppOps)); + } } public MessagingLinearLayout getMessagingLinearLayout() { diff --git a/core/java/com/android/internal/widget/InlinePresentationStyleUtils.java b/core/java/com/android/internal/widget/InlinePresentationStyleUtils.java new file mode 100644 index 000000000000..264c8bd2303a --- /dev/null +++ b/core/java/com/android/internal/widget/InlinePresentationStyleUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import android.annotation.NonNull; +import android.os.Bundle; + +import java.util.Objects; +import java.util.Set; + +/** + * Utility methods relating to inline presentation UI. + */ +public final class InlinePresentationStyleUtils { + + /** + * Returns true if the two bundles are deeply equal. + * + * Each input bundle may represent a UI style in the + * {@link android.widget.inline.InlinePresentationSpec} or the extra + * request info in the {@link android.view.inputmethod.InlineSuggestionsRequest} + * + * Note: this method should not be called in the framework process for security reasons. + */ + public static boolean bundleEquals(@NonNull Bundle bundle1, @NonNull Bundle bundle2) { + if (bundle1 == bundle2) { + return true; + } + if (bundle1 == null || bundle2 == null) { + return false; + } + if (bundle1.size() != bundle2.size()) { + return false; + } + Set<String> keys = bundle1.keySet(); + for (String key : keys) { + Object value1 = bundle1.get(key); + Object value2 = bundle2.get(key); + if (value1 instanceof Bundle && value2 instanceof Bundle + && !bundleEquals((Bundle) value1, (Bundle) value2)) { + return false; + } else if (!Objects.equals(value1, value2)) { + return false; + } + } + return true; + } + + /** + * Private ctor to avoid constructing the class. + */ + private InlinePresentationStyleUtils() { + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index eae614546dfb..451363f6bd3d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3788,10 +3788,9 @@ android:protectionLevel="signature|installer" /> <!-- @SystemApi Allows an application to manage the holders of a role. - @hide - STOPSHIP b/145526313: Remove wellbeing protection flag from MANAGE_ROLE_HOLDERS. --> + @hide --> <permission android:name="android.permission.MANAGE_ROLE_HOLDERS" - android:protectionLevel="signature|installer|wellbeing" /> + android:protectionLevel="signature|installer" /> <!-- @SystemApi Allows an application to observe role holder changes. @hide --> @@ -4713,12 +4712,6 @@ <permission android:name="android.permission.MANAGE_SOUND_TRIGGER" android:protectionLevel="signature|privileged" /> - <!-- Allows preempting sound trigger recognitions for the sake of capturing audio on - implementations which do not support running both concurrently. - @hide --> - <permission android:name="android.permission.PREEMPT_SOUND_TRIGGER" - android:protectionLevel="signature|privileged" /> - <!-- Must be required by system/priv apps implementing sound trigger detection services @hide @SystemApi --> diff --git a/core/res/res/drawable/conversation_unread_bg.xml b/core/res/res/drawable/conversation_unread_bg.xml new file mode 100644 index 000000000000..d3e00cfbf8b1 --- /dev/null +++ b/core/res/res/drawable/conversation_unread_bg.xml @@ -0,0 +1,19 @@ +<!-- + ~ 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. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="20sp" /> + <solid android:color="@android:color/white" /> +</shape>
\ No newline at end of file diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml index 46d3d1326920..b9ca29276cf0 100644 --- a/core/res/res/layout/notification_template_material_conversation.xml +++ b/core/res/res/layout/notification_template_material_conversation.xml @@ -136,6 +136,7 @@ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title" android:textSize="16sp" android:singleLine="true" + android:layout_weight="1" /> <TextView @@ -166,6 +167,18 @@ /> <ImageView + android:id="@+id/alerted_icon" + android:layout_width="@dimen/notification_alerted_size" + android:layout_height="@dimen/notification_alerted_size" + android:layout_gravity="center" + android:layout_marginStart="4dp" + android:paddingTop="2dp" + android:scaleType="fitCenter" + android:visibility="gone" + android:contentDescription="@string/notification_alerted_content_description" + android:src="@drawable/ic_notifications_alerted"/> + + <ImageView android:id="@+id/profile_badge" android:layout_width="@dimen/notification_badge_size" android:layout_height="@dimen/notification_badge_size" @@ -176,6 +189,44 @@ android:visibility="gone" android:contentDescription="@string/notification_work_profile_content_description" /> + <LinearLayout + android:id="@+id/app_ops" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:paddingTop="3dp" + android:layout_marginStart="2dp" + android:orientation="horizontal" > + <ImageButton + android:layout_marginStart="4dp" + android:id="@+id/camera" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_camera" + android:background="?android:selectableItemBackgroundBorderless" + android:visibility="gone" + android:contentDescription="@string/notification_appops_camera_active" + /> + <ImageButton + android:id="@+id/mic" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_mic" + android:background="?android:selectableItemBackgroundBorderless" + android:layout_marginStart="4dp" + android:visibility="gone" + android:contentDescription="@string/notification_appops_microphone_active" + /> + <ImageButton + android:id="@+id/overlay" + android:layout_width="?attr/notificationHeaderIconSize" + android:layout_height="?attr/notificationHeaderIconSize" + android:src="@drawable/ic_alert_window_layer" + android:background="?android:selectableItemBackgroundBorderless" + android:layout_marginStart="4dp" + android:visibility="gone" + android:contentDescription="@string/notification_appops_overlay_active" + /> + </LinearLayout> </LinearLayout> <!-- App Name --> @@ -199,10 +250,8 @@ android:clipChildren="false" /> </com.android.internal.widget.RemeasuringLinearLayout> - <!-- Unread Count --> - <!-- <TextView /> --> - <!-- This is where the expand button will be placed when collapsed--> + <!-- This is where the expand button container will be placed when collapsed--> </com.android.internal.widget.RemeasuringLinearLayout> <include layout="@layout/notification_template_smart_reply_container" @@ -238,6 +287,21 @@ android:clipToPadding="false" android:clipChildren="false" /> + <!-- Unread Count --> + <TextView + android:id="@+id/conversation_unread_count" + android:layout_width="33sp" + android:layout_height="wrap_content" + android:layout_marginEnd="11dp" + android:layout_gravity="center" + android:gravity="center" + android:padding="2dp" + android:visibility="gone" + android:textAppearance="@style/TextAppearance.DeviceDefault.Notification" + android:textColor="#FFFFFF" + android:textSize="12sp" + android:background="@drawable/conversation_unread_bg" + /> <com.android.internal.widget.NotificationExpandButton android:id="@+id/expand_button" android:layout_width="@dimen/notification_header_expand_icon_size" @@ -246,6 +310,6 @@ android:drawable="@drawable/ic_expand_notification" android:clickable="false" android:importantForAccessibility="no" - /> + /> </LinearLayout> </com.android.internal.widget.ConversationLayout> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index bba2f1fcbe38..b1bba53bd7ab 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4421,7 +4421,7 @@ <string name="config_customSessionPolicyProvider"></string> <!-- The max scale for the wallpaper when it's zoomed in --> - <item name="config_wallpaperMaxScale" format="float" type="dimen">1.15</item> + <item name="config_wallpaperMaxScale" format="float" type="dimen">1.10</item> <!-- Package name that will receive an explicit manifest broadcast for android.os.action.POWER_SAVE_MODE_CHANGED. --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e2e65dd465ae..c9c498ed9b38 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5442,6 +5442,9 @@ <!-- Conversation Title fallback if the there is no name provided in a group chat conversation [CHAR LIMIT=40]--> <string name="conversation_title_fallback_group_chat">Group Conversation</string> + <!-- Number of unread messages displayed on a conversation notification, when greater-than-or-equal-to 100 [CHAR LIMIT=3]--> + <string name="unread_convo_overflow"><xliff:g id="max_unread_count" example="99">%1$d</xliff:g>+</string> + <!-- ResolverActivity - profile tabs --> <!-- Label of a tab on a screen. A user can tap this tap to switch to the 'Personal' view (that shows their personal content) if they have a work profile on their device. [CHAR LIMIT=NONE] --> <string name="resolver_personal_tab">Personal</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 31b9f0a4354f..ec8058235912 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3898,6 +3898,8 @@ <java-symbol type="dimen" name="button_padding_horizontal_material" /> <java-symbol type="dimen" name="button_inset_horizontal_material" /> <java-symbol type="layout" name="conversation_face_pile_layout" /> + <java-symbol type="id" name="conversation_unread_count" /> + <java-symbol type="string" name="unread_convo_overflow" /> <!-- Intent resolver and share sheet --> <java-symbol type="string" name="resolver_personal_tab" /> diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index 9d251dac7c78..5f12bf04d931 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -229,7 +229,7 @@ public class InsetsAnimationControlImplTest { doAnswer(invocation -> { mController.applyChangeInsets(mInsetsState); return null; - }).when(mMockController).scheduleApplyChangeInsets(); + }).when(mMockController).scheduleApplyChangeInsets(any()); mController.finish(true /* shown */); assertEquals(Insets.of(0, 100, 100, 0), mController.getCurrentInsets()); verify(mMockController).notifyFinished(eq(mController), eq(true /* shown */)); diff --git a/data/etc/platform.xml b/data/etc/platform.xml index f63ec6bd04c3..6af887d401f6 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -162,7 +162,6 @@ <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" /> <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" /> <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" /> - <assign-permission name="android.permission.PREEMPT_SOUND_TRIGGER" uid="audioserver" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" /> <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" /> diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl index a2f9ee906c03..5925d380115c 100644 --- a/media/java/android/media/IMediaRouter2Manager.aidl +++ b/media/java/android/media/IMediaRouter2Manager.aidl @@ -24,8 +24,8 @@ import android.media.RoutingSessionInfo; * {@hide} */ oneway interface IMediaRouter2Manager { - void notifySessionCreated(in RoutingSessionInfo sessionInfo); - void notifySessionsUpdated(); + void notifySessionCreated(int requestId, in RoutingSessionInfo sessionInfo); + void notifySessionUpdated(in RoutingSessionInfo sessionInfo); void notifyPreferredFeaturesChanged(String packageName, in List<String> preferredFeatures); void notifyRoutesAdded(in List<MediaRoute2Info> routes); void notifyRoutesRemoved(in List<MediaRoute2Info> routes); diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 88bcd6aaad95..b694fd059bfa 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -75,6 +75,8 @@ public final class MediaRouter2Manager { final ConcurrentMap<String, List<String>> mPreferredFeaturesMap = new ConcurrentHashMap<>(); private final AtomicInteger mNextRequestId = new AtomicInteger(1); + private final CopyOnWriteArrayList<TransferRequest> mTransferRequests = + new CopyOnWriteArrayList<>(); /** * Gets an instance of media router manager that controls media route of other applications. @@ -328,6 +330,9 @@ public final class MediaRouter2Manager { if (client != null) { try { int requestId = mNextRequestId.getAndIncrement(); + //TODO: Ensure that every request is eventually removed. + mTransferRequests.add(new TransferRequest(requestId, sessionInfo, route)); + mMediaRouterService.requestCreateSessionWithManager( client, requestId, sessionInfo.getClientPackageName(), route); } catch (RemoteException ex) { @@ -446,6 +451,77 @@ public final class MediaRouter2Manager { } } + void createSessionOnHandler(int requestId, RoutingSessionInfo sessionInfo) { + TransferRequest matchingRequest = null; + for (TransferRequest request : mTransferRequests) { + if (request.mRequestId == requestId) { + matchingRequest = request; + break; + } + } + + if (matchingRequest == null) { + return; + } + + mTransferRequests.remove(matchingRequest); + + MediaRoute2Info requestedRoute = matchingRequest.mTargetRoute; + + if (sessionInfo == null) { + notifyTransferFailed(matchingRequest.mOldSessionInfo, requestedRoute); + return; + } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) { + Log.w(TAG, "The session does not contain the requested route. " + + "(requestedRouteId=" + requestedRoute.getId() + + ", actualRoutes=" + sessionInfo.getSelectedRoutes() + + ")"); + notifyTransferFailed(matchingRequest.mOldSessionInfo, requestedRoute); + return; + } else if (!TextUtils.equals(requestedRoute.getProviderId(), + sessionInfo.getProviderId())) { + Log.w(TAG, "The session's provider ID does not match the requested route's. " + + "(requested route's providerId=" + requestedRoute.getProviderId() + + ", actual providerId=" + sessionInfo.getProviderId() + + ")"); + notifyTransferFailed(matchingRequest.mOldSessionInfo, requestedRoute); + return; + } + notifyTransferred(matchingRequest.mOldSessionInfo, sessionInfo); + } + + void handleFailureOnHandler(int requestId, int reason) { + TransferRequest matchingRequest = null; + for (TransferRequest request : mTransferRequests) { + if (request.mRequestId == requestId) { + matchingRequest = request; + break; + } + } + + if (matchingRequest != null) { + mTransferRequests.remove(matchingRequest); + notifyTransferFailed(matchingRequest.mOldSessionInfo, matchingRequest.mTargetRoute); + return; + } + notifyRequestFailed(reason); + } + + void handleSessionsUpdated(RoutingSessionInfo sessionInfo) { + for (TransferRequest request : mTransferRequests) { + String sessionId = request.mOldSessionInfo.getId(); + if (!TextUtils.equals(sessionId, sessionInfo.getId())) { + continue; + } + if (sessionInfo.getSelectedRoutes().contains(request.mTargetRoute.getId())) { + notifyTransferred(request.mOldSessionInfo, sessionInfo); + mTransferRequests.remove(request); + break; + } + } + notifySessionUpdated(sessionInfo); + } + private void notifyRoutesAdded(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( @@ -467,16 +543,9 @@ public final class MediaRouter2Manager { } } - void notifySessionCreated(RoutingSessionInfo sessionInfo) { + void notifySessionUpdated(RoutingSessionInfo sessionInfo) { for (CallbackRecord record : mCallbackRecords) { - record.mExecutor.execute(() -> record.mCallback.onSessionCreated( - new RoutingController(sessionInfo))); - } - } - - void notifySessionInfosChanged() { - for (CallbackRecord record : mCallbackRecords) { - record.mExecutor.execute(() -> record.mCallback.onSessionsUpdated()); + record.mExecutor.execute(() -> record.mCallback.onSessionUpdated(sessionInfo)); } } @@ -569,7 +638,7 @@ public final class MediaRouter2Manager { * * @see #getSelectedRoutes(RoutingSessionInfo) * @see #getSelectableRoutes(RoutingSessionInfo) - * @see Callback#onSessionsUpdated() + * @see Callback#onSessionUpdated(RoutingSessionInfo) */ public void selectRoute(@NonNull RoutingSessionInfo sessionInfo, @NonNull MediaRoute2Info route) { @@ -614,7 +683,7 @@ public final class MediaRouter2Manager { * * @see #getSelectedRoutes(RoutingSessionInfo) * @see #getDeselectableRoutes(RoutingSessionInfo) - * @see Callback#onSessionsUpdated() + * @see Callback#onSessionUpdated(RoutingSessionInfo) */ public void deselectRoute(@NonNull RoutingSessionInfo sessionInfo, @NonNull MediaRoute2Info route) { @@ -667,13 +736,15 @@ public final class MediaRouter2Manager { return; } + int requestId = mNextRequestId.getAndIncrement(); + mTransferRequests.add(new TransferRequest(requestId, sessionInfo, route)); + Client client; synchronized (sLock) { client = mClient; } if (client != null) { try { - int requestId = mNextRequestId.getAndIncrement(); mMediaRouterService.transferToRouteWithManager( mClient, requestId, sessionInfo.getId(), route); } catch (RemoteException ex) { @@ -884,20 +955,12 @@ public final class MediaRouter2Manager { public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {} /** - * Called when a routing session is created. - * - * @param controller the controller to control the created session + * Called when a session is changed. + * @param sessionInfo the updated session */ - public void onSessionCreated(@NonNull RoutingController controller) {} + public void onSessionUpdated(@NonNull RoutingSessionInfo sessionInfo) {} /** - * Called when at least one session info is changed. - * Call {@link #getActiveSessions()} to get current active session info. - */ - public void onSessionsUpdated() {} - - //TODO: Call this. - /** * Called when media is transferred. * * @param oldSession the previous session @@ -906,7 +969,6 @@ public final class MediaRouter2Manager { public void onTransferred(@NonNull RoutingSessionInfo oldSession, @Nullable RoutingSessionInfo newSession) { } - //TODO: Call this. /** * Called when {@link #transfer(RoutingSessionInfo, MediaRoute2Info)} fails. */ @@ -971,25 +1033,37 @@ public final class MediaRouter2Manager { } } + static final class TransferRequest { + public final int mRequestId; + public final RoutingSessionInfo mOldSessionInfo; + public final MediaRoute2Info mTargetRoute; + + TransferRequest(int requestId, @NonNull RoutingSessionInfo oldSessionInfo, + @NonNull MediaRoute2Info targetRoute) { + mRequestId = requestId; + mOldSessionInfo = oldSessionInfo; + mTargetRoute = targetRoute; + } + } + class Client extends IMediaRouter2Manager.Stub { @Override - public void notifySessionCreated(RoutingSessionInfo sessionInfo) { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionCreated, - MediaRouter2Manager.this, sessionInfo)); + public void notifySessionCreated(int requestId, RoutingSessionInfo sessionInfo) { + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::createSessionOnHandler, + MediaRouter2Manager.this, requestId, sessionInfo)); } @Override - public void notifySessionsUpdated() { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionInfosChanged, - MediaRouter2Manager.this)); - // do nothing + public void notifySessionUpdated(RoutingSessionInfo sessionInfo) { + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::handleSessionsUpdated, + MediaRouter2Manager.this, sessionInfo)); } @Override public void notifyRequestFailed(int requestId, int reason) { // Note: requestId is not used. - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRequestFailed, - MediaRouter2Manager.this, reason)); + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::handleFailureOnHandler, + MediaRouter2Manager.this, requestId, reason)); } @Override diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java index 2e038e665520..68f2964dbeb2 100644 --- a/media/java/android/media/RouteDiscoveryPreference.java +++ b/media/java/android/media/RouteDiscoveryPreference.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import java.util.ArrayList; import java.util.Collection; @@ -29,6 +30,7 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; /** * A media route discovery preference describing the features of routes that media router @@ -169,8 +171,9 @@ public final class RouteDiscoveryPreference implements Parcelable { Bundle mExtras; public Builder(@NonNull List<String> preferredFeatures, boolean activeScan) { - mPreferredFeatures = new ArrayList<>(Objects.requireNonNull(preferredFeatures, - "preferredFeatures must not be null")); + Objects.requireNonNull(preferredFeatures, "preferredFeatures must not be null"); + mPreferredFeatures = preferredFeatures.stream().filter(str -> !TextUtils.isEmpty(str)) + .collect(Collectors.toList()); mActiveScan = activeScan; } @@ -211,8 +214,9 @@ public final class RouteDiscoveryPreference implements Parcelable { */ @NonNull public Builder setPreferredFeatures(@NonNull List<String> preferredFeatures) { - mPreferredFeatures = new ArrayList<>(Objects.requireNonNull(preferredFeatures, - "preferredFeatures must not be null")); + Objects.requireNonNull(preferredFeatures, "preferredFeatures must not be null"); + mPreferredFeatures = preferredFeatures.stream().filter(str -> !TextUtils.isEmpty(str)) + .collect(Collectors.toList()); return this; } diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl b/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl index 80333070b7ce..06c39071cdf5 100644 --- a/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl +++ b/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl @@ -39,10 +39,4 @@ interface ISoundTriggerMiddlewareService { * one of the handles from the returned list. */ ISoundTriggerModule attach(int handle, ISoundTriggerCallback callback); - - /** - * Notify the service that external input capture is taking place. This may cause some of the - * active recognitions to be aborted. - */ - void setExternalCaptureState(boolean active); }
\ No newline at end of file diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java index 77e8f9719294..6ca564fb34cc 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java @@ -231,9 +231,10 @@ public class MediaRouter2ManagerTest { addRouterCallback(new RouteCallback() {}); addManagerCallback(new MediaRouter2Manager.Callback() { @Override - public void onSessionCreated(MediaRouter2Manager.RoutingController controller) { - if (TextUtils.equals(mPackageName, controller.getClientPackageName()) - && createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)) { + public void onTransferred(RoutingSessionInfo oldSessionInfo, + RoutingSessionInfo newSessionInfo) { + if (TextUtils.equals(mPackageName, newSessionInfo.getClientPackageName()) + && newSessionInfo.getSelectedRoutes().contains(ROUTE_ID1)) { latch.countDown(); } } @@ -268,8 +269,9 @@ public class MediaRouter2ManagerTest { addManagerCallback(new MediaRouter2Manager.Callback() { @Override - public void onSessionCreated(MediaRouter2Manager.RoutingController controller) { - assertNotNull(controller); + public void onTransferred(RoutingSessionInfo oldSessionInfo, + RoutingSessionInfo newSessionInfo) { + assertNotNull(newSessionInfo); onSessionCreatedLatch.countDown(); } }); @@ -352,8 +354,9 @@ public class MediaRouter2ManagerTest { // create a controller addManagerCallback(new MediaRouter2Manager.Callback() { @Override - public void onSessionCreated(MediaRouter2Manager.RoutingController controller) { - assertNotNull(controller); + public void onTransferred(RoutingSessionInfo oldSessionInfo, + RoutingSessionInfo newSessionInfo) { + assertNotNull(newSessionInfo); onSessionCreatedLatch.countDown(); } }); @@ -383,13 +386,12 @@ public class MediaRouter2ManagerTest { addManagerCallback(new MediaRouter2Manager.Callback() { @Override - public void onSessionsUpdated() { - List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName); - if (sessions.size() != 2) { + public void onSessionUpdated(RoutingSessionInfo updatedSessionInfo) { + if (!TextUtils.equals(sessionInfo.getId(), updatedSessionInfo.getId())) { return; } - if (sessions.get(1).getVolume() == targetVolume) { + if (updatedSessionInfo.getVolume() == targetVolume) { volumeChangedLatch.countDown(); } } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 02604d870986..cd45fc908db4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -65,6 +65,7 @@ import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; @@ -225,6 +226,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt KeyguardIndicationController keyguardIndicationController, DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager, + Lazy<NotificationShadeDepthController> depthControllerLazy, /* Car Settings injected components. */ CarNavigationBarController carNavigationBarController) { super( @@ -304,6 +306,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt phoneStatusBarPolicy, keyguardIndicationController, dismissCallbackRegistry, + depthControllerLazy, statusBarTouchableRegionManager); mUserSwitcherController = userSwitcherController; mScrimController = scrimController; diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java index 1baa1f6891ee..e163173daefb 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java @@ -51,6 +51,7 @@ import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; @@ -200,6 +201,7 @@ public class CarStatusBarModule { KeyguardIndicationController keyguardIndicationController, DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager, + Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy, CarNavigationBarController carNavigationBarController) { return new CarStatusBar( context, @@ -278,6 +280,7 @@ public class CarStatusBarModule { keyguardIndicationController, dismissCallbackRegistry, statusBarTouchableRegionManager, + notificationShadeDepthControllerLazy, carNavigationBarController); } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index cc4ee89f2208..f6368c466e91 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -109,7 +109,7 @@ public class AssistManager { protected static final String CONSTRAINED_KEY = "should_constrain"; public static final int INVOCATION_TYPE_GESTURE = 1; - public static final int INVOCATION_TYPE_ACTIVE_EDGE = 2; + public static final int INVOCATION_TYPE_OTHER = 2; public static final int INVOCATION_TYPE_VOICE = 3; public static final int INVOCATION_TYPE_QUICK_SEARCH_BAR = 4; public static final int INVOCATION_HOME_BUTTON_LONG_PRESS = 5; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 5c66462f2a5b..496456deccee 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -125,6 +125,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList // Custom options so there is no activity transition animation ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(), 0 /* enterResId */, 0 /* exitResId */); + options.setTaskAlwaysOnTop(true); // Post to keep the lifecycle normal post(() -> { if (DEBUG_BUBBLE_EXPANDED_VIEW) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index a978cad1127a..d7322a04ba49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -39,7 +39,6 @@ import com.android.systemui.statusbar.phone.PanelExpansionListener import com.android.systemui.statusbar.policy.KeyguardStateController import java.io.FileDescriptor import java.io.PrintWriter -import java.lang.IllegalArgumentException import javax.inject.Inject import javax.inject.Singleton import kotlin.math.max @@ -74,6 +73,15 @@ class NotificationShadeDepthController @Inject constructor( @VisibleForTesting var globalActionsSpring = DepthAnimation() + @VisibleForTesting + var brightnessMirrorSpring = DepthAnimation() + var brightnessMirrorVisible: Boolean = false + set(value) { + field = value + brightnessMirrorSpring.animateTo(if (value) blurUtils.blurRadiusOfRatio(1f) + else 0) + } + /** * Blur radius of the wake-up animation on this frame. */ @@ -91,7 +99,9 @@ class NotificationShadeDepthController @Inject constructor( val updateBlurCallback = Choreographer.FrameCallback { updateScheduled = false - val blur = max(max(shadeSpring.radius, wakeAndUnlockBlurRadius), globalActionsSpring.radius) + var shadeRadius = max(shadeSpring.radius, wakeAndUnlockBlurRadius) + shadeRadius = (shadeRadius * (1f - brightnessMirrorSpring.ratio)).toInt() + val blur = max(shadeRadius, globalActionsSpring.radius) blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur) try { wallpaperManager.setWallpaperZoomOut(root.windowToken, @@ -148,6 +158,7 @@ class NotificationShadeDepthController @Inject constructor( if (isDozing) { shadeSpring.finishIfRunning() globalActionsSpring.finishIfRunning() + brightnessMirrorSpring.finishIfRunning() } } } @@ -199,6 +210,7 @@ class NotificationShadeDepthController @Inject constructor( it.increaseIndent() it.println("shadeRadius: ${shadeSpring.radius}") it.println("globalActionsRadius: ${globalActionsSpring.radius}") + it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}") it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius") } } @@ -212,7 +224,12 @@ class NotificationShadeDepthController @Inject constructor( * Blur radius visible on the UI, in pixels. */ var radius = 0 - private set + + /** + * Depth ratio of the current blur radius. + */ + val ratio + get() = blurUtils.ratioOfBlurRadius(radius) /** * Radius that we're animating to. @@ -239,7 +256,7 @@ class NotificationShadeDepthController @Inject constructor( init { springAnimation.spring = SpringForce(0.0f) springAnimation.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY - springAnimation.spring.stiffness = SpringForce.STIFFNESS_MEDIUM + springAnimation.spring.stiffness = SpringForce.STIFFNESS_HIGH springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java index 564d8bc14c8c..3f74aaf3abf5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java @@ -38,10 +38,12 @@ public class NotificationUiAdjustment { public final String key; public final List<Notification.Action> smartActions; public final List<CharSequence> smartReplies; + public final boolean isConversation; @VisibleForTesting NotificationUiAdjustment( - String key, List<Notification.Action> smartActions, List<CharSequence> smartReplies) { + String key, List<Notification.Action> smartActions, List<CharSequence> smartReplies, + boolean isConversation) { this.key = key; this.smartActions = smartActions == null ? Collections.emptyList() @@ -49,12 +51,14 @@ public class NotificationUiAdjustment { this.smartReplies = smartReplies == null ? Collections.emptyList() : smartReplies; + this.isConversation = isConversation; } public static NotificationUiAdjustment extractFromNotificationEntry( NotificationEntry entry) { return new NotificationUiAdjustment( - entry.getKey(), entry.getSmartActions(), entry.getSmartReplies()); + entry.getKey(), entry.getSmartActions(), entry.getSmartReplies(), + entry.getRanking().isConversation()); } public static boolean needReinflate( @@ -63,6 +67,9 @@ public class NotificationUiAdjustment { if (oldAdjustment == newAdjustment) { return false; } + if (oldAdjustment.isConversation != newAdjustment.isConversation) { + return true; + } if (areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions)) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessor.kt deleted file mode 100644 index 6be0fff38f2b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotificationProcessor.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification - -import android.app.Notification -import android.content.pm.LauncherApps -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import javax.inject.Inject - -class ConversationNotificationProcessor @Inject constructor( - private val launcherApps: LauncherApps -) { - fun processNotification(entry: NotificationEntry, recoveredBuilder: Notification.Builder) { - val messagingStyle = recoveredBuilder.style as? Notification.MessagingStyle ?: return - messagingStyle.conversationType = - if (entry.ranking.channel.isImportantConversation) - Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT - else - Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL - entry.ranking.shortcutInfo?.let { shortcutInfo -> - messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo) - shortcutInfo.shortLabel?.let { shortLabel -> - messagingStyle.conversationTitle = shortLabel - } - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt new file mode 100644 index 000000000000..7ef1d0eba3f1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification + +import android.app.Notification +import android.content.Context +import android.content.pm.LauncherApps +import android.service.notification.NotificationListenerService.Ranking +import android.service.notification.NotificationListenerService.RankingMap +import com.android.internal.statusbar.NotificationVisibility +import com.android.internal.widget.ConversationLayout +import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.NotificationContentView +import java.util.concurrent.ConcurrentHashMap +import javax.inject.Inject +import javax.inject.Singleton + +/** Populates additional information in conversation notifications */ +class ConversationNotificationProcessor @Inject constructor( + private val launcherApps: LauncherApps, + private val conversationNotificationManager: ConversationNotificationManager +) { + fun processNotification(entry: NotificationEntry, recoveredBuilder: Notification.Builder) { + val messagingStyle = recoveredBuilder.style as? Notification.MessagingStyle ?: return + messagingStyle.conversationType = + if (entry.ranking.channel.isImportantConversation) + Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT + else + Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL + entry.ranking.shortcutInfo?.let { shortcutInfo -> + messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo) + shortcutInfo.shortLabel?.let { shortLabel -> + messagingStyle.conversationTitle = shortLabel + } + } + messagingStyle.unreadMessageCount = + conversationNotificationManager.getUnreadCount(entry, recoveredBuilder) + } +} + +/** + * Tracks state related to conversation notifications, and updates the UI of existing notifications + * when necessary. + */ +@Singleton +class ConversationNotificationManager @Inject constructor( + private val notificationEntryManager: NotificationEntryManager, + private val context: Context +) { + // Need this state to be thread safe, since it's accessed from the ui thread + // (NotificationEntryListener) and a bg thread (NotificationContentInflater) + private val states = ConcurrentHashMap<String, ConversationState>() + + private var notifPanelCollapsed = true + + init { + notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener { + + override fun onNotificationRankingUpdated(rankingMap: RankingMap) { + fun getLayouts(view: NotificationContentView) = + sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild) + val ranking = Ranking() + states.keys.asSequence() + .mapNotNull { notificationEntryManager.getActiveNotificationUnfiltered(it) } + .forEach { entry -> + if (rankingMap.getRanking(entry.sbn.key, ranking) && + ranking.isConversation) { + val important = ranking.channel.isImportantConversation + entry.row?.layouts?.asSequence() + ?.flatMap(::getLayouts) + ?.mapNotNull { it as? ConversationLayout } + ?.forEach { it.setIsImportantConversation(important) } + } + } + } + + override fun onEntryInflated(entry: NotificationEntry) { + if (!entry.ranking.isConversation) return + fun updateCount(isExpanded: Boolean) { + if (isExpanded && !notifPanelCollapsed) { + resetCount(entry.key) + entry.row?.let(::resetBadgeUi) + } + } + entry.row?.setOnExpansionChangedListener(::updateCount) + updateCount(entry.row?.isExpanded == true) + } + + override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry) + + override fun onEntryRemoved( + entry: NotificationEntry, + visibility: NotificationVisibility?, + removedByUser: Boolean, + reason: Int + ) = removeTrackedEntry(entry) + }) + } + + fun getUnreadCount(entry: NotificationEntry, recoveredBuilder: Notification.Builder): Int = + states.compute(entry.key) { _, state -> + val newCount = state?.run { + val old = Notification.Builder.recoverBuilder(context, notification) + val increment = Notification + .areStyledNotificationsVisiblyDifferent(old, recoveredBuilder) + if (increment) unreadCount + 1 else unreadCount + } ?: 1 + ConversationState(newCount, entry.sbn.notification) + }!!.unreadCount + + fun onNotificationPanelExpandStateChanged(isCollapsed: Boolean) { + notifPanelCollapsed = isCollapsed + if (isCollapsed) return + + // When the notification panel is expanded, reset the counters of any expanded + // conversations + val expanded = states + .asSequence() + .mapNotNull { (key, _) -> + notificationEntryManager.getActiveNotificationUnfiltered(key) + ?.let { entry -> + if (entry.row?.isExpanded == true) key to entry + else null + } + } + .toMap() + states.replaceAll { key, state -> + if (expanded.contains(key)) state.copy(unreadCount = 0) + else state + } + // Update UI separate from the replaceAll call, since ConcurrentHashMap may re-run the + // lambda if threads are in contention. + expanded.values.asSequence().mapNotNull { it.row }.forEach(::resetBadgeUi) + } + + private fun resetCount(key: String) { + states.compute(key) { _, state -> state?.copy(unreadCount = 0) } + } + + private fun removeTrackedEntry(entry: NotificationEntry) { + states.remove(entry.key) + } + + private fun resetBadgeUi(row: ExpandableNotificationRow): Unit = + (row.layouts?.asSequence() ?: emptySequence()) + .mapNotNull { layout -> layout.contractedChild as? ConversationLayout } + .forEach { convoLayout -> convoLayout.setUnreadCount(0) } + + private data class ConversationState(val unreadCount: Int, val notification: Notification) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index b90cfa8ae25e..c9cc67009399 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -249,6 +249,7 @@ public class NotifCollection implements Dumpable { stats.notificationVisibility); } catch (RemoteException e) { // system process is dead if we're here. + mLogger.logRemoteExceptionOnNotificationClear(entry.getKey(), e); } } } @@ -277,6 +278,7 @@ public class NotifCollection implements Dumpable { mStatusBarService.onClearAllNotifications(userId); } catch (RemoteException e) { // system process is dead if we're here. + mLogger.logRemoteExceptionOnClearAllNotifications(e); } final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs()); @@ -743,6 +745,6 @@ public class NotifCollection implements Dumpable { @Retention(RetentionPolicy.SOURCE) public @interface CancellationReason {} - public static final int REASON_NOT_CANCELED = -1; + static final int REASON_NOT_CANCELED = -1; public static final int REASON_UNKNOWN = 0; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt index 8675cca3cffe..ef302f682df8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt @@ -16,11 +16,13 @@ package com.android.systemui.statusbar.notification.collection.notifcollection +import android.os.RemoteException import android.service.notification.NotificationListenerService.RankingMap import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.LogLevel.WARNING +import com.android.systemui.log.LogLevel.WTF import com.android.systemui.log.dagger.NotificationLog import javax.inject.Inject @@ -92,6 +94,23 @@ class NotifCollectionLogger @Inject constructor( buffer.log(TAG, DEBUG, { str1 = entry }, { " $str1" }) } } + + fun logRemoteExceptionOnNotificationClear(key: String, e: RemoteException) { + buffer.log(TAG, WTF, { + str1 = key + str2 = e.toString() + }, { + "RemoteException while attempting to clear $str1:\n$str2" + }) + } + + fun logRemoteExceptionOnClearAllNotifications(e: RemoteException) { + buffer.log(TAG, WTF, { + str1 = e.toString() + }, { + "RemoteException while attempting to clear all notifications:\n$str1" + }) + } } -private const val TAG = "NotifCollection"
\ No newline at end of file +private const val TAG = "NotifCollection" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 7deabf79a6dd..19b5f5c79ea2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -107,6 +107,7 @@ import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAn import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; @@ -136,7 +137,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ public interface LayoutListener { void onLayout(); + } + /** Listens for changes to the expansion state of this row. */ + public interface OnExpansionChangedListener { + void onExpansionChanged(boolean isExpanded); } private StatusBarStateController mStatusbarStateController; @@ -323,6 +328,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mWasChildInGroupWhenRemoved; private NotificationInlineImageResolver mImageResolver; private NotificationMediaManager mMediaManager; + @Nullable private OnExpansionChangedListener mExpansionChangedListener; private SystemNotificationAsyncTask mSystemNotificationAsyncTask = new SystemNotificationAsyncTask(); @@ -351,6 +357,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return isSystemNotification; } + public NotificationContentView[] getLayouts() { + return Arrays.copyOf(mLayouts, mLayouts.length); + } + @Override public boolean isGroupExpansionChanging() { if (isChildInGroup()) { @@ -1659,8 +1669,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public void showAppOpsIcons(ArraySet<Integer> activeOps) { - if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) { - mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps); + if (mIsSummaryWithChildren) { + mChildrenContainer.showAppOpsIcons(activeOps); } mPrivateLayout.showAppOpsIcons(activeOps); mPublicLayout.showAppOpsIcons(activeOps); @@ -1687,8 +1697,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private final Runnable mExpireRecentlyAlertedFlag = () -> applyAudiblyAlertedRecently(false); private void applyAudiblyAlertedRecently(boolean audiblyAlertedRecently) { - if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) { - mChildrenContainer.getHeaderView().setRecentlyAudiblyAlerted(audiblyAlertedRecently); + if (mIsSummaryWithChildren) { + mChildrenContainer.setRecentlyAudiblyAlerted(audiblyAlertedRecently); } mPrivateLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently); mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently); @@ -2911,9 +2921,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mIsSummaryWithChildren) { mChildrenContainer.onExpansionChanged(); } + if (mExpansionChangedListener != null) { + mExpansionChangedListener.onExpansionChanged(nowExpanded); + } } } + public void setOnExpansionChangedListener(@Nullable OnExpansionChangedListener listener) { + mExpansionChangedListener = listener; + } + @Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 9b9225e0bde0..8efdc1b56e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -1468,27 +1468,27 @@ public class NotificationContentView extends FrameLayout { } public void showAppOpsIcons(ArraySet<Integer> activeOps) { - if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) { - mContractedWrapper.getNotificationHeader().showAppOpsIcons(activeOps); + if (mContractedChild != null) { + mContractedWrapper.showAppOpsIcons(activeOps); } - if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) { - mExpandedWrapper.getNotificationHeader().showAppOpsIcons(activeOps); + if (mExpandedChild != null) { + mExpandedWrapper.showAppOpsIcons(activeOps); } - if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) { - mHeadsUpWrapper.getNotificationHeader().showAppOpsIcons(activeOps); + if (mHeadsUpChild != null) { + mHeadsUpWrapper.showAppOpsIcons(activeOps); } } /** Sets whether the notification being displayed audibly alerted the user. */ public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) { - if (mContractedChild != null && mContractedWrapper.getNotificationHeader() != null) { - mContractedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted); + if (mContractedChild != null) { + mContractedWrapper.setRecentlyAudiblyAlerted(audiblyAlerted); } - if (mExpandedChild != null && mExpandedWrapper.getNotificationHeader() != null) { - mExpandedWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted); + if (mExpandedChild != null) { + mExpandedWrapper.setRecentlyAudiblyAlerted(audiblyAlerted); } - if (mHeadsUpChild != null && mHeadsUpWrapper.getNotificationHeader() != null) { - mHeadsUpWrapper.getNotificationHeader().setRecentlyAudiblyAlerted(audiblyAlerted); + if (mHeadsUpChild != null) { + mHeadsUpWrapper.setRecentlyAudiblyAlerted(audiblyAlerted); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java index 82e5f0a3b130..8d675f86c343 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java @@ -47,6 +47,18 @@ public abstract class StackScrollerDecorView extends ExpandableView { } }; + private boolean mSecondaryAnimating = false; + private final Runnable mSecondaryVisibilityEndRunnable = () -> { + mSecondaryAnimating = false; + // If we were on screen, become GONE to avoid touches + if (mSecondaryView == null) return; + if (getVisibility() != View.GONE + && mSecondaryView.getVisibility() != View.GONE + && !mIsSecondaryVisible) { + mSecondaryView.setVisibility(View.GONE); + } + }; + public StackScrollerDecorView(Context context, AttributeSet attrs) { super(context, attrs); setClipChildren(false); @@ -88,9 +100,11 @@ public abstract class StackScrollerDecorView extends ExpandableView { private void setContentVisible(boolean contentVisible, boolean animate) { if (mContentVisible != contentVisible) { mContentAnimating = animate; - setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable); mContentVisible = contentVisible; - } if (!mContentAnimating) { + setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable); + } + + if (!mContentAnimating) { mContentVisibilityEndRunnable.run(); } } @@ -136,8 +150,13 @@ public abstract class StackScrollerDecorView extends ExpandableView { */ public void setSecondaryVisible(boolean nowVisible, boolean animate) { if (mIsSecondaryVisible != nowVisible) { - setViewVisible(mSecondaryView, nowVisible, animate, null /* endRunnable */); + mSecondaryAnimating = animate; mIsSecondaryVisible = nowVisible; + setViewVisible(mSecondaryView, nowVisible, animate, mSecondaryVisibilityEndRunnable); + } + + if (!mSecondaryAnimating) { + mSecondaryVisibilityEndRunnable.run(); } } @@ -170,6 +189,12 @@ public abstract class StackScrollerDecorView extends ExpandableView { if (view == null) { return; } + + // Make sure we're visible so animations work + if (view.getVisibility() != View.VISIBLE) { + view.setVisibility(View.VISIBLE); + } + // cancel any previous animations view.animate().cancel(); float endValue = nowVisible ? 1.0f : 0.0f; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 7808a4b2dc74..0c311b403c48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.row.wrapper; import static com.android.systemui.statusbar.notification.TransformState.TRANSFORM_Y; +import android.annotation.NonNull; +import android.app.AppOpsManager; import android.app.Notification; import android.content.Context; import android.util.ArraySet; @@ -60,6 +62,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { protected NotificationHeaderView mNotificationHeader; private TextView mHeaderText; private ImageView mWorkProfileImage; + private View mCameraIcon; + private View mMicIcon; + private View mOverlayIcon; + private View mAppOps; + private View mAudiblyAlertedIcon; private boolean mIsLowPriority; private boolean mTransformLowPriorityTitle; @@ -107,6 +114,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); + mCameraIcon = mView.findViewById(com.android.internal.R.id.camera); + mMicIcon = mView.findViewById(com.android.internal.R.id.mic); + mOverlayIcon = mView.findViewById(com.android.internal.R.id.overlay); + mAppOps = mView.findViewById(com.android.internal.R.id.app_ops); + mAudiblyAlertedIcon = mView.findViewById(com.android.internal.R.id.alerted_icon); if (mNotificationHeader != null) { mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd); mColor = mNotificationHeader.getOriginalIconColor(); @@ -114,8 +126,35 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } private void addAppOpsOnClickListener(ExpandableNotificationRow row) { + View.OnClickListener listener = row.getAppOpsOnClickListener(); if (mNotificationHeader != null) { - mNotificationHeader.setAppOpsOnClickListener(row.getAppOpsOnClickListener()); + mNotificationHeader.setAppOpsOnClickListener(listener); + } + mAppOps.setOnClickListener(listener); + mCameraIcon.setOnClickListener(listener); + mMicIcon.setOnClickListener(listener); + mOverlayIcon.setOnClickListener(listener); + } + + /** + * Shows or hides 'app op in use' icons based on app usage. + */ + @Override + public void showAppOpsIcons(ArraySet<Integer> appOps) { + if (appOps == null) { + return; + } + if (mOverlayIcon != null) { + mOverlayIcon.setVisibility(appOps.contains(AppOpsManager.OP_SYSTEM_ALERT_WINDOW) + ? View.VISIBLE : View.GONE); + } + if (mCameraIcon != null) { + mCameraIcon.setVisibility(appOps.contains(AppOpsManager.OP_CAMERA) + ? View.VISIBLE : View.GONE); + } + if (mMicIcon != null) { + mMicIcon.setVisibility(appOps.contains(AppOpsManager.OP_RECORD_AUDIO) + ? View.VISIBLE : View.GONE); } } @@ -184,6 +223,18 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mHeaderText); } + if (mCameraIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mCameraIcon); + } + if (mMicIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mMicIcon); + } + if (mOverlayIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mOverlayIcon); + } + if (mAudiblyAlertedIcon != null) { + mTransformationHelper.addViewTransformingToSimilar(mAudiblyAlertedIcon); + } } @Override @@ -195,6 +246,13 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } @Override + public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) { + if (mAudiblyAlertedIcon != null) { + mAudiblyAlertedIcon.setVisibility(audiblyAlerted ? View.VISIBLE : View.GONE); + } + } + + @Override public NotificationHeaderView getNotificationHeader() { return mNotificationHeader; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index e4fb2f7c42d4..fa7f282be74a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -29,6 +29,7 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; +import android.util.ArraySet; import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; @@ -95,6 +96,14 @@ public abstract class NotificationViewWrapper implements TransformableView { public void onContentUpdated(ExpandableNotificationRow row) { } + /** + * Show a set of app opp icons in the layout. + * + * @param appOps which app ops to show + */ + public void showAppOpsIcons(ArraySet<Integer> appOps) { + } + public void onReinflated() { if (shouldClearBackgroundOnReapply()) { mBackgroundColor = 0; @@ -362,4 +371,10 @@ public abstract class NotificationViewWrapper implements TransformableView { public int getExtraMeasureHeight() { return 0; } + + /** + * Set the view to have recently visibly alerted. + */ + public void setRecentlyAudiblyAlerted(boolean audiblyAlerted) { + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 3d0bf3f4c1c6..400e794b820b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -22,6 +22,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.ColorDrawable; import android.service.notification.StatusBarNotification; +import android.util.ArraySet; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.NotificationHeaderView; @@ -1265,4 +1266,27 @@ public class NotificationChildrenContainer extends ViewGroup { mHeaderVisibleAmount = headerVisibleAmount; mCurrentHeaderTranslation = (int) ((1.0f - headerVisibleAmount) * mTranslationForHeader); } + + /** + * Show a set of app opp icons in the layout. + * + * @param appOps which app ops to show + */ + public void showAppOpsIcons(ArraySet<Integer> appOps) { + if (mNotificationHeaderWrapper != null) { + mNotificationHeaderWrapper.showAppOpsIcons(appOps); + } + if (mNotificationHeaderWrapperLowPriority != null) { + mNotificationHeaderWrapperLowPriority.showAppOpsIcons(appOps); + } + } + + public void setRecentlyAudiblyAlerted(boolean audiblyAlertedRecently) { + if (mNotificationHeaderWrapper != null) { + mNotificationHeaderWrapper.setRecentlyAudiblyAlerted(audiblyAlertedRecently); + } + if (mNotificationHeaderWrapperLowPriority != null) { + mNotificationHeaderWrapperLowPriority.setRecentlyAudiblyAlerted(audiblyAlertedRecently); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 98ba6e5b88a0..31797d1faa61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; +import static java.lang.Float.isNaN; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; @@ -86,6 +88,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.AnimatableProperty; +import com.android.systemui.statusbar.notification.ConversationNotificationManager; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; @@ -238,6 +241,7 @@ public class NotificationPanelViewController extends PanelViewController { private final PulseExpansionHandler mPulseExpansionHandler; private final KeyguardBypassController mKeyguardBypassController; private final KeyguardUpdateMonitor mUpdateMonitor; + private final ConversationNotificationManager mConversationNotificationManager; private KeyguardAffordanceHelper mAffordanceHelper; private KeyguardUserSwitcher mKeyguardUserSwitcher; @@ -451,7 +455,8 @@ public class NotificationPanelViewController extends PanelViewController { ActivityManager activityManager, ZenModeController zenModeController, ConfigurationController configurationController, FlingAnimationUtils.Builder flingAnimationUtilsBuilder, - StatusBarTouchableRegionManager statusBarTouchableRegionManager) { + StatusBarTouchableRegionManager statusBarTouchableRegionManager, + ConversationNotificationManager conversationNotificationManager) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); @@ -509,6 +514,7 @@ public class NotificationPanelViewController extends PanelViewController { mShadeController = shadeController; mLockscreenUserManager = notificationLockscreenUserManager; mEntryManager = notificationEntryManager; + mConversationNotificationManager = conversationNotificationManager; mView.setBackgroundColor(Color.TRANSPARENT); OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener(); @@ -2005,7 +2011,12 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected float getOverExpansionAmount() { - return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */); + float result = mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */); + if (isNaN(result)) { + Log.wtf(TAG, "OverExpansionAmount is NaN!"); + } + + return result; } @Override @@ -2143,6 +2154,7 @@ public class NotificationPanelViewController extends PanelViewController { super.onExpandingFinished(); mNotificationStackScroller.onExpansionStopped(); mHeadsUpManager.onExpandingFinished(); + mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed()); mIsExpanding = false; if (isFullyCollapsed()) { DejankUtils.postAfterTraversal(new Runnable() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 83cc4e33e2db..f7d403f667cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static java.lang.Float.isNaN; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -638,6 +640,9 @@ public abstract class PanelViewController { } public void setExpandedHeightInternal(float h) { + if (isNaN(h)) { + Log.wtf(TAG, "ExpandedHeight set to NaN"); + } if (mExpandLatencyTracking && h != 0f) { DejankUtils.postAfterTraversal( () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 119662c75857..ae2c78b121ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -176,6 +176,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; @@ -589,6 +590,7 @@ public class StatusBar extends SystemUI implements DemoMode, private ActivityLaunchAnimator mActivityLaunchAnimator; protected StatusBarNotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; + private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; private final BubbleController mBubbleController; private final BubbleController.BubbleExpandListener mBubbleExpandListener; @@ -679,6 +681,7 @@ public class StatusBar extends SystemUI implements DemoMode, PhoneStatusBarPolicy phoneStatusBarPolicy, KeyguardIndicationController keyguardIndicationController, DismissCallbackRegistry dismissCallbackRegistry, + Lazy<NotificationShadeDepthController> notificationShadeDepthControllerLazy, StatusBarTouchableRegionManager statusBarTouchableRegionManager) { super(context); mNotificationsController = notificationsController; @@ -735,6 +738,7 @@ public class StatusBar extends SystemUI implements DemoMode, mScreenPinningRequest = screenPinningRequest; mDozeScrimController = dozeScrimController; mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; + mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy; mVolumeComponent = volumeComponent; mCommandQueue = commandQueue; mRecentsOptional = recentsOptional; @@ -1135,6 +1139,7 @@ public class StatusBar extends SystemUI implements DemoMode, mBrightnessMirrorController = new BrightnessMirrorController( mNotificationShadeWindowView, mNotificationPanelViewController, + mNotificationShadeDepthControllerLazy.get(), (visible) -> { mBrightnessMirrorVisible = visible; updateScrimController(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index bbc7e7ab8c06..b81a5198b498 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -51,6 +51,7 @@ import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.SuperStatusBarViewFactory; @@ -197,6 +198,7 @@ public interface StatusBarPhoneModule { UserInfoControllerImpl userInfoControllerImpl, PhoneStatusBarPolicy phoneStatusBarPolicy, KeyguardIndicationController keyguardIndicationController, + Lazy<NotificationShadeDepthController> notificationShadeDepthController, DismissCallbackRegistry dismissCallbackRegistry, StatusBarTouchableRegionManager statusBarTouchableRegionManager) { return new StatusBar( @@ -276,6 +278,7 @@ public interface StatusBarPhoneModule { phoneStatusBarPolicy, keyguardIndicationController, dismissCallbackRegistry, + notificationShadeDepthController, statusBarTouchableRegionManager); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index d62da10de3d5..78111fb61fd0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -24,6 +24,7 @@ import android.view.View; import android.widget.FrameLayout; import com.android.systemui.R; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; @@ -39,16 +40,19 @@ public class BrightnessMirrorController private final NotificationShadeWindowView mStatusBarWindow; private final Consumer<Boolean> mVisibilityCallback; private final NotificationPanelViewController mNotificationPanel; + private final NotificationShadeDepthController mDepthController; private final ArraySet<BrightnessMirrorListener> mBrightnessMirrorListeners = new ArraySet<>(); private final int[] mInt2Cache = new int[2]; private View mBrightnessMirror; public BrightnessMirrorController(NotificationShadeWindowView statusBarWindow, NotificationPanelViewController notificationPanelViewController, + NotificationShadeDepthController notificationShadeDepthController, @NonNull Consumer<Boolean> visibilityCallback) { mStatusBarWindow = statusBarWindow; mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror); mNotificationPanel = notificationPanelViewController; + mDepthController = notificationShadeDepthController; mNotificationPanel.setPanelAlphaEndAction(() -> { mBrightnessMirror.setVisibility(View.INVISIBLE); }); @@ -59,11 +63,13 @@ public class BrightnessMirrorController mBrightnessMirror.setVisibility(View.VISIBLE); mVisibilityCallback.accept(true); mNotificationPanel.setPanelAlpha(0, true /* animate */); + mDepthController.setBrightnessMirrorVisible(true); } public void hideMirror() { mVisibilityCallback.accept(false); mNotificationPanel.setPanelAlpha(255, true /* animate */); + mDepthController.setBrightnessMirrorVisible(false); } public void setLocation(View original) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index 956bfd0337de..1e3636b4ed63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -45,7 +45,6 @@ import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.doThrow import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit -import java.lang.IllegalArgumentException @RunWith(AndroidTestingRunner::class) @RunWithLooper @@ -64,6 +63,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { @Mock private lateinit var viewRootImpl: ViewRootImpl @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation + @Mock private lateinit var brightnessSpring: NotificationShadeDepthController.DepthAnimation @JvmField @Rule val mockitoRule = MockitoJUnit.rule() private lateinit var statusBarStateListener: StatusBarStateController.StateListener @@ -83,6 +83,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { keyguardStateController, choreographer, wallpaperManager, notificationShadeWindowController, dumpManager) notificationShadeDepthController.shadeSpring = shadeSpring + notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring notificationShadeDepthController.globalActionsSpring = globalActionsSpring notificationShadeDepthController.root = root @@ -134,6 +135,30 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { verify(wallpaperManager).setWallpaperZoomOut(any(), anyFloat()) } + @Test + fun brightnessMirrorVisible_whenVisible() { + notificationShadeDepthController.brightnessMirrorVisible = true + verify(brightnessSpring).animateTo(eq(maxBlur), any()) + } + + @Test + fun brightnessMirrorVisible_whenHidden() { + notificationShadeDepthController.brightnessMirrorVisible = false + verify(brightnessSpring).animateTo(eq(0), any()) + } + + @Test + fun brightnessMirror_hidesShadeBlur() { + // Brightness mirror is fully visible + `when`(brightnessSpring.ratio).thenReturn(1f) + // And shade is blurred + `when`(shadeSpring.radius).thenReturn(maxBlur) + + notificationShadeDepthController.updateBlurCallback.doFrame(0) + verify(notificationShadeWindowController).setBackgroundBlurRadius(0) + verify(blurUtils).applyBlur(safeEq(viewRootImpl), eq(0)) + } + private fun <T : Any> safeEq(value: T): T { return eq(value) ?: value } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java index d00be568cbff..3c9c9cca1619 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java @@ -188,6 +188,30 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase { .isFalse(); } + @Test + public void needReinflate_bothConversation() { + assertThat(NotificationUiAdjustment.needReinflate( + createUiAdjustmentForConversation("first", true), + createUiAdjustmentForConversation("first", true))) + .isFalse(); + } + + @Test + public void needReinflate_neitherConversation() { + assertThat(NotificationUiAdjustment.needReinflate( + createUiAdjustmentForConversation("first", false), + createUiAdjustmentForConversation("first", false))) + .isFalse(); + } + + @Test + public void needReinflate_differentIsConversation() { + assertThat(NotificationUiAdjustment.needReinflate( + createUiAdjustmentForConversation("first", false), + createUiAdjustmentForConversation("first", true))) + .isTrue(); + } + private Notification.Action.Builder createActionBuilder( String title, int drawableRes, PendingIntent pendingIntent) { return new Notification.Action.Builder( @@ -200,11 +224,16 @@ public class NotificationUiAdjustmentTest extends SysuiTestCase { private NotificationUiAdjustment createUiAdjustmentFromSmartActions( String key, List<Notification.Action> actions) { - return new NotificationUiAdjustment(key, actions, null); + return new NotificationUiAdjustment(key, actions, null, false); } private NotificationUiAdjustment createUiAdjustmentFromSmartReplies( String key, CharSequence[] replies) { - return new NotificationUiAdjustment(key, null, Arrays.asList(replies)); + return new NotificationUiAdjustment(key, null, Arrays.asList(replies), false); + } + + private NotificationUiAdjustment createUiAdjustmentForConversation( + String key, boolean isConversation) { + return new NotificationUiAdjustment(key, null, null, isConversation); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index c356e0d16512..cb379208eb94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -229,22 +229,19 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { @Test public void testShowAppOpsIcons_header() { - NotificationHeaderView mockHeader = mock(NotificationHeaderView.class); - NotificationContentView publicLayout = mock(NotificationContentView.class); mGroupRow.setPublicLayout(publicLayout); NotificationContentView privateLayout = mock(NotificationContentView.class); mGroupRow.setPrivateLayout(privateLayout); NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class); when(mockContainer.getNotificationChildCount()).thenReturn(1); - when(mockContainer.getHeaderView()).thenReturn(mockHeader); mGroupRow.setChildrenContainer(mockContainer); ArraySet<Integer> ops = new ArraySet<>(); ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS); mGroupRow.showAppOpsIcons(ops); - verify(mockHeader, times(1)).showAppOpsIcons(ops); + verify(mockContainer, times(1)).showAppOpsIcons(ops); verify(privateLayout, times(1)).showAppOpsIcons(ops); verify(publicLayout, times(1)).showAppOpsIcons(ops); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index 84c651368dc9..0f268984a996 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -76,14 +76,14 @@ public class NotificationContentViewTest extends SysuiTestCase { @Test @UiThreadTest public void testShowAppOpsIcons() { - NotificationHeaderView mockContracted = mock(NotificationHeaderView.class); - when(mockContracted.findViewById(com.android.internal.R.id.notification_header)) + View mockContracted = mock(View.class); + when(mockContracted.findViewById(com.android.internal.R.id.mic)) .thenReturn(mockContracted); - NotificationHeaderView mockExpanded = mock(NotificationHeaderView.class); - when(mockExpanded.findViewById(com.android.internal.R.id.notification_header)) + View mockExpanded = mock(View.class); + when(mockExpanded.findViewById(com.android.internal.R.id.mic)) .thenReturn(mockExpanded); - NotificationHeaderView mockHeadsUp = mock(NotificationHeaderView.class); - when(mockHeadsUp.findViewById(com.android.internal.R.id.notification_header)) + View mockHeadsUp = mock(View.class); + when(mockHeadsUp.findViewById(com.android.internal.R.id.mic)) .thenReturn(mockHeadsUp); mView.setContractedChild(mockContracted); @@ -91,11 +91,11 @@ public class NotificationContentViewTest extends SysuiTestCase { mView.setHeadsUpChild(mockHeadsUp); ArraySet<Integer> ops = new ArraySet<>(); - ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS); + ops.add(AppOpsManager.OP_RECORD_AUDIO); mView.showAppOpsIcons(ops); - verify(mockContracted, times(1)).showAppOpsIcons(ops); - verify(mockExpanded, times(1)).showAppOpsIcons(ops); - verify(mockHeadsUp, times(1)).showAppOpsIcons(any()); + verify(mockContracted, times(1)).setVisibility(View.VISIBLE); + verify(mockExpanded, times(1)).setVisibility(View.VISIBLE); + verify(mockHeadsUp, times(1)).setVisibility(View.VISIBLE); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 13bf38c7f0f3..4b09aa687073 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -64,6 +64,7 @@ import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.ConversationNotificationManager; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; @@ -169,6 +170,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { private ZenModeController mZenModeController; @Mock private ConfigurationController mConfigurationController; + @Mock + private ConversationNotificationManager mConversationNotificationManager; private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; private NotificationPanelViewController mNotificationPanelViewController; @@ -223,7 +226,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController, - mFlingAnimationUtilsBuilder, mStatusBarTouchableRegionManager); + mFlingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + mConversationNotificationManager); mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager, mNotificationShelf, mNotificationAreaController, mScrimController); mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 679ac2224128..b905bddb98f5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -102,6 +102,7 @@ import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.RemoteInputController; @@ -249,6 +250,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private ExtensionController mExtensionController; @Mock private UserInfoControllerImpl mUserInfoControllerImpl; @Mock private PhoneStatusBarPolicy mPhoneStatusBarPolicy; + @Mock private Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; private ShadeController mShadeController; private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private InitController mInitController = new InitController(); @@ -404,6 +406,7 @@ public class StatusBarTest extends SysuiTestCase { mPhoneStatusBarPolicy, mKeyguardIndicationController, mDismissCallbackRegistry, + mNotificationShadeDepthControllerLazy, mStatusBarTouchableRegionManager); when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn( diff --git a/services/core/Android.bp b/services/core/Android.bp index 052026c2746a..5faed43dd6e6 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -126,6 +126,7 @@ java_library_static { "android.hardware.rebootescrow-java", "android.hardware.soundtrigger-V2.3-java", "android.hidl.manager-V1.2-java", + "capture_state_listener-aidl-java", "dnsresolver_aidl_interface-V2-java", "netd_event_listener_interface-java", "overlayable_policy_aidl-java", diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java index a6811e3070b2..ea607cb750de 100644 --- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java @@ -35,6 +35,8 @@ import android.os.UserManager; import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; +import android.view.WindowInsets; +import android.view.WindowManager; import android.widget.ImageView; import android.widget.TextView; @@ -50,12 +52,19 @@ import com.android.internal.R; final class CarUserSwitchingDialog extends UserSwitchingDialog { private static final String TAG = "ActivityManagerCarUserSwitchingDialog"; + private View mView; public CarUserSwitchingDialog(ActivityManagerService service, Context context, UserInfo oldUser, UserInfo newUser, boolean aboveSystem, String switchingFromSystemUserMessage, String switchingToSystemUserMessage) { super(service, context, oldUser, newUser, aboveSystem, switchingFromSystemUserMessage, switchingToSystemUserMessage); + + // {@link UserSwitchingDialog} uses {@link WindowManager.LayoutParams.TYPE_SYSTEM_ERROR} + // when trying to show dialog above system. That window type has been deprecated and since + // this is a system dialog, hence, it makes sense to put this in System Dialog Window. + // This window also automatically shows status bar. + getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); } @Override @@ -65,7 +74,7 @@ final class CarUserSwitchingDialog extends UserSwitchingDialog { Resources res = getContext().getResources(); // Custom view due to alignment and font size requirements getContext().setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert_UserSwitchingDialog); - View view = LayoutInflater.from(getContext()).inflate( + mView = LayoutInflater.from(getContext()).inflate( R.layout.car_user_switching_dialog, null); @@ -75,11 +84,11 @@ final class CarUserSwitchingDialog extends UserSwitchingDialog { if (bitmap != null) { CircleFramedDrawable drawable = CircleFramedDrawable.getInstance(bitmap, res.getDimension(R.dimen.car_fullscreen_user_pod_image_avatar_height)); - ((ImageView) view.findViewById(R.id.user_loading_avatar)) + ((ImageView) mView.findViewById(R.id.user_loading_avatar)) .setImageDrawable(drawable); } - TextView msgView = view.findViewById(R.id.user_loading); + TextView msgView = mView.findViewById(R.id.user_loading); // TODO(b/145132885): use constant from CarSettings boolean showInfo = "true".equals(Settings.Global.getString( @@ -92,7 +101,17 @@ final class CarUserSwitchingDialog extends UserSwitchingDialog { } else { msgView.setText(res.getString(R.string.car_loading_profile)); } - setView(view); + setView(mView); + } + + @Override + public void show() { + super.show(); + hideNavigationBar(); + } + + private void hideNavigationBar() { + mView.getWindowInsetsController().hide(WindowInsets.Type.navigationBars()); } /** diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index b546120e2b95..c2c79d361996 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -324,7 +324,7 @@ import java.io.PrintWriter; } /*package*/ void setBluetoothScoOn(boolean on, String eventSource) { - //Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource); + //Log.i(TAG, "setBluetoothScoOn: " + on + " " + eventSource); synchronized (mDeviceStateLock) { if (on) { // do not accept SCO ON if SCO audio is not connected diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 36332c0ad25c..93d1bede9de8 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -58,6 +58,7 @@ public class BtHelper { } // List of clients having issued a SCO start request + @GuardedBy("BtHelper.this") private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>(); // BluetoothHeadset API to control SCO connection @@ -356,9 +357,8 @@ public class BtHelper { // client is created. final long ident = Binder.clearCallingIdentity(); try { - eventSource += " client count before=" + client.getCount(); AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); - client.incCount(scoAudioMode); + client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); } catch (NullPointerException e) { Log.e(TAG, "Null ScoClient", e); } @@ -375,9 +375,15 @@ public class BtHelper { // and this must be done on behalf of system server to make sure permissions are granted. final long ident = Binder.clearCallingIdentity(); if (client != null) { - eventSource += " client count before=" + client.getCount(); AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); - client.decCount(); + client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + SCO_MODE_VIRTUAL_CALL); + // If a disconnection is pending, the client will be removed whne clearAllScoClients() + // is called form receiveBtEvent() + if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ + && mScoAudioState != SCO_STATE_DEACTIVATING) { + client.remove(false /*stop */, true /*unregister*/); + } } Binder.restoreCallingIdentity(ident); } @@ -657,96 +663,40 @@ public class BtHelper { @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void scoClientDied(Object obj) { final ScoClient client = (ScoClient) obj; + client.remove(true /*stop*/, false /*unregister*/); Log.w(TAG, "SCO client died"); - int index = mScoClients.indexOf(client); - if (index < 0) { - Log.w(TAG, "unregistered SCO client died"); - } else { - client.clearCount(true); - mScoClients.remove(client); - } } private class ScoClient implements IBinder.DeathRecipient { private IBinder mCb; // To be notified of client's death private int mCreatorPid; - private int mStartcount; // number of SCO connections started by this client ScoClient(IBinder cb) { mCb = cb; mCreatorPid = Binder.getCallingPid(); - mStartcount = 0; - } - - @Override - public void binderDied() { - // process this from DeviceBroker's message queue to take the right locks since - // this event can impact SCO mode and requires querying audio mode stack - mDeviceBroker.postScoClientDied(this); } - // @GuardedBy("AudioDeviceBroker.mSetModeLock") - // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - @GuardedBy("BtHelper.this") - void incCount(int scoAudioMode) { - if (!requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode)) { - Log.e(TAG, "Request sco connected with scoAudioMode(" - + scoAudioMode + ") failed"); - return; - } - if (mStartcount == 0) { - try { - mCb.linkToDeath(this, 0); - } catch (RemoteException e) { - // client has already died! - Log.w(TAG, "ScoClient incCount() could not link to " - + mCb + " binder death"); - } - } - mStartcount++; - } - - // @GuardedBy("AudioDeviceBroker.mSetModeLock") - // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - @GuardedBy("BtHelper.this") - void decCount() { - if (mStartcount == 0) { - Log.w(TAG, "ScoClient.decCount() already 0"); - } else { - mStartcount--; - if (mStartcount == 0) { - try { - mCb.unlinkToDeath(this, 0); - } catch (NoSuchElementException e) { - Log.w(TAG, "decCount() going to 0 but not registered to binder"); - } - } - if (!requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0)) { - Log.w(TAG, "Request sco disconnected with scoAudioMode(0) failed"); - } + public void registerDeathRecipient() { + try { + mCb.linkToDeath(this, 0); + } catch (RemoteException e) { + Log.w(TAG, "ScoClient could not link to " + mCb + " binder death"); } } - // @GuardedBy("AudioDeviceBroker.mSetModeLock") - // @GuardedBy("AudioDeviceBroker.mDeviceStateLock") - @GuardedBy("BtHelper.this") - void clearCount(boolean stopSco) { - if (mStartcount != 0) { - try { - mCb.unlinkToDeath(this, 0); - } catch (NoSuchElementException e) { - Log.w(TAG, "clearCount() mStartcount: " - + mStartcount + " != 0 but not registered to binder"); - } - } - mStartcount = 0; - if (stopSco) { - requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0); + public void unregisterDeathRecipient() { + try { + mCb.unlinkToDeath(this, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "ScoClient could not not unregistered to binder"); } } - int getCount() { - return mStartcount; + @Override + public void binderDied() { + // process this from DeviceBroker's message queue to take the right locks since + // this event can impact SCO mode and requires querying audio mode stack + mDeviceBroker.postScoClientDied(this); } IBinder getBinder() { @@ -757,23 +707,14 @@ public class BtHelper { return mCreatorPid; } - private int totalCount() { - int count = 0; - for (ScoClient mScoClient : mScoClients) { - count += mScoClient.getCount(); - } - return count; - } - // @GuardedBy("AudioDeviceBroker.mSetModeLock") //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") private boolean requestScoState(int state, int scoAudioMode) { checkScoAudioState(); - int clientCount = totalCount(); - if (clientCount != 0) { + if (mScoClients.size() != 1) { Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode - + ", clientCount=" + clientCount); + + ", num SCO clients=" + mScoClients.size()); return true; } if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { @@ -842,12 +783,14 @@ public class BtHelper { mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); break; + case SCO_STATE_ACTIVE_INTERNAL: + Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return"); + break; default: Log.w(TAG, "requestScoState: failed to connect in state " + mScoAudioState + ", scoAudioMode=" + scoAudioMode); broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); return false; - } } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { switch (mScoAudioState) { @@ -893,6 +836,18 @@ public class BtHelper { } return true; } + + @GuardedBy("BtHelper.this") + void remove(boolean stop, boolean unregister) { + if (unregister) { + unregisterDeathRecipient(); + } + if (stop) { + requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, + SCO_MODE_VIRTUAL_CALL); + } + mScoClients.remove(this); + } } //----------------------------------------------------- @@ -946,6 +901,7 @@ public class BtHelper { } + @GuardedBy("BtHelper.this") private ScoClient getScoClient(IBinder cb, boolean create) { for (ScoClient existingClient : mScoClients) { if (existingClient.getBinder() == cb) { @@ -954,6 +910,7 @@ public class BtHelper { } if (create) { ScoClient newClient = new ScoClient(cb); + newClient.registerDeathRecipient(); mScoClients.add(newClient); return newClient; } @@ -964,18 +921,16 @@ public class BtHelper { //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") @GuardedBy("BtHelper.this") private void clearAllScoClients(int exceptPid, boolean stopSco) { - ScoClient savedClient = null; + final ArrayList<ScoClient> clients = new ArrayList<ScoClient>(); for (ScoClient cl : mScoClients) { if (cl.getPid() != exceptPid) { - cl.clearCount(stopSco); - } else { - savedClient = cl; + clients.add(cl); } } - mScoClients.clear(); - if (savedClient != null) { - mScoClients.add(savedClient); + for (ScoClient cl : clients) { + cl.remove(stopSco, true /*unregister*/); } + } private boolean getBluetoothHeadset() { diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 0bba1723931d..b28350d51e9e 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -493,7 +493,7 @@ class MediaRouter2ServiceImpl { } } - //TODO: Review this is handling multi-user properly. + //TODO(b/136703681): Review this is handling multi-user properly. void switchUser() { synchronized (mLock) { int userId = ActivityManager.getCurrentUser(); @@ -568,7 +568,9 @@ class MediaRouter2ServiceImpl { UserRecord userRecord = routerRecord.mUserRecord; userRecord.mRouterRecords.remove(routerRecord); - //TODO: update discovery request + userRecord.mHandler.sendMessage( + obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler, + userRecord.mHandler)); routerRecord.dispose(); disposeUserIfNeededLocked(userRecord); // since router removed from user } @@ -793,7 +795,7 @@ class MediaRouter2ServiceImpl { } long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); - //TODO: Use MediaRouter2's OnCreateSessionListener to send proper session hints. + //TODO(b/152851868): Use MediaRouter2's OnCreateSessionListener to send session hints. routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::requestCreateSessionOnHandler, routerRecord.mUserRecord.mHandler, @@ -1146,7 +1148,6 @@ class MediaRouter2ServiceImpl { return mSessionToRouterMap.get(uniqueSessionId); } - //TODO: notify session info updates private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) { int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId()); MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo(); @@ -1323,7 +1324,7 @@ class MediaRouter2ServiceImpl { return true; } - //TODO: Handle RCN case. + //TODO(b/152950479): Handle RCN case. if (routerRecord == null) { Slog.w(TAG, "Ignoring " + description + " route from unknown router."); return false; @@ -1403,7 +1404,8 @@ class MediaRouter2ServiceImpl { private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider, long uniqueRequestId, @NonNull RoutingSessionInfo sessionInfo) { - notifySessionCreatedToManagers(getManagers(), sessionInfo); + notifySessionCreatedToManagers(getManagers(), + toOriginalRequestId(uniqueRequestId), sessionInfo); if (uniqueRequestId == REQUEST_ID_NONE) { // The session is created without any matching request. @@ -1457,7 +1459,7 @@ class MediaRouter2ServiceImpl { private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo) { List<IMediaRouter2Manager> managers = getManagers(); - notifySessionInfosChangedToManagers(managers); + notifySessionInfoChangedToManagers(managers, sessionInfo); // For system provider, notify all routers. if (provider == mSystemProvider) { @@ -1480,7 +1482,7 @@ class MediaRouter2ServiceImpl { private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo) { List<IMediaRouter2Manager> managers = getManagers(); - notifySessionInfosChangedToManagers(managers); + notifySessionInfoChangedToManagers(managers, sessionInfo); RouterRecord routerRecord = mSessionToRouterMap.get(sessionInfo.getId()); if (routerRecord == null) { @@ -1558,7 +1560,8 @@ class MediaRouter2ServiceImpl { private void notifySessionCreationFailedToRouter(@NonNull RouterRecord routerRecord, int requestId) { try { - routerRecord.mRouter.notifySessionCreated(requestId, /* sessionInfo= */ null); + routerRecord.mRouter.notifySessionCreated(requestId, + /* sessionInfo= */ null); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify router of the session creation failure." + " Router probably died.", ex); @@ -1731,10 +1734,10 @@ class MediaRouter2ServiceImpl { } private void notifySessionCreatedToManagers(@NonNull List<IMediaRouter2Manager> managers, - @NonNull RoutingSessionInfo sessionInfo) { + int requestId, @NonNull RoutingSessionInfo sessionInfo) { for (IMediaRouter2Manager manager : managers) { try { - manager.notifySessionCreated(sessionInfo); + manager.notifySessionCreated(requestId, sessionInfo); } catch (RemoteException ex) { Slog.w(TAG, "notifySessionCreatedToManagers: " + "failed to notify. Manager probably died.", ex); @@ -1742,11 +1745,12 @@ class MediaRouter2ServiceImpl { } } - private void notifySessionInfosChangedToManagers( - @NonNull List<IMediaRouter2Manager> managers) { + private void notifySessionInfoChangedToManagers( + @NonNull List<IMediaRouter2Manager> managers, + @NonNull RoutingSessionInfo sessionInfo) { for (IMediaRouter2Manager manager : managers) { try { - manager.notifySessionsUpdated(); + manager.notifySessionUpdated(sessionInfo); } catch (RemoteException ex) { Slog.w(TAG, "notifySessionInfosChangedToManagers: " + "failed to notify. Manager probably died.", ex); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 32cfaf614ab9..dbb246e9fbe8 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -20,6 +20,7 @@ import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID; import static android.app.NotificationManager.IMPORTANCE_NONE; import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; +import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES; @@ -1798,6 +1799,7 @@ public class PreferencesHelper implements RankingConfig { .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES); final PackagePreferences r = mPackagePreferences.valueAt(i); event.writeInt(r.uid); + event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); event.writeInt(r.importance); event.writeInt(r.visibility); event.writeInt(r.lockedAppFields); @@ -1825,6 +1827,7 @@ public class PreferencesHelper implements RankingConfig { StatsEvent.Builder event = StatsEvent.newBuilder() .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES); event.writeInt(r.uid); + event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); event.writeString(channel.getId()); event.writeString(channel.getName().toString()); event.writeString(channel.getDescription()); @@ -1856,6 +1859,7 @@ public class PreferencesHelper implements RankingConfig { StatsEvent.Builder event = StatsEvent.newBuilder() .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES); event.writeInt(r.uid); + event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); event.writeString(groupChannel.getId()); event.writeString(groupChannel.getName().toString()); event.writeString(groupChannel.getDescription()); diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 805d91852d8e..09b782d768d2 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -674,7 +674,8 @@ public class AppsFilter { Trace.endSection(); if (callingPkgSetting != null) { - if (!mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) { + if (callingPkgSetting.pkg != null + && !mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) { if (DEBUG_LOGGING) { log(callingSetting, targetPkgSetting, "DISABLED"); } @@ -682,7 +683,8 @@ public class AppsFilter { } } else { for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) { - if (!mFeatureConfig.packageIsEnabled(callingSharedPkgSettings.valueAt(i).pkg)) { + final AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).pkg; + if (pkg != null && !mFeatureConfig.packageIsEnabled(pkg)) { if (DEBUG_LOGGING) { log(callingSetting, targetPkgSetting, "DISABLED"); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8f63131e1327..f96ab1d9a042 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1065,6 +1065,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean onlyCore; public OverlayConfig overlayConfig; public PackageDexOptimizer packageDexOptimizer; + public PackageParser2.Callback packageParserCallback; public IPermissionManager permissionManagerService; public PendingPackageBroadcasts pendingPackageBroadcasts; public PackageManagerInternal pmInternal; @@ -2779,6 +2780,7 @@ public class PackageManagerService extends IPackageManager.Stub mOnlyCore = testParams.onlyCore; mOverlayConfig = testParams.overlayConfig; mPackageDexOptimizer = testParams.packageDexOptimizer; + mPackageParserCallback = testParams.packageParserCallback; mPendingBroadcasts = testParams.pendingPackageBroadcasts; mPermissionManagerService = testParams.permissionManagerService; mPmInternal = testParams.pmInternal; diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 8eb391454df8..432d7f335ebc 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -44,6 +44,7 @@ import java.util.Set; public class PackageSetting extends PackageSettingBase { int appId; + @Nullable public AndroidPackage pkg; /** * WARNING. The object reference is important. We perform integer equality and NOT diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 8768ab0a683b..8d53d1554619 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -2385,6 +2385,30 @@ public class ShortcutService extends IShortcutService.Stub { } } + public boolean isSharingShortcut(int callingUserId, @NonNull String callingPackage, + @NonNull String packageName, @NonNull String shortcutId, int userId, + @NonNull IntentFilter filter) { + verifyCaller(callingPackage, callingUserId); + enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, + "isSharingShortcut"); + + synchronized (mLock) { + throwIfUserLockedL(userId); + throwIfUserLockedL(callingUserId); + + final List<ShortcutManager.ShareShortcutInfo> matchedTargets = + getPackageShortcutsLocked(packageName, userId) + .getMatchingShareTargets(filter); + final int matchedSize = matchedTargets.size(); + for (int i = 0; i < matchedSize; i++) { + if (matchedTargets.get(i).getShortcutInfo().getId().equals(shortcutId)) { + return true; + } + } + } + return false; + } + @GuardedBy("mLock") private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName, @UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) { @@ -2969,6 +2993,18 @@ public class ShortcutService extends IShortcutService.Stub { callingPackage, intentFilter, userId).getList(); } + @Override + public boolean isSharingShortcut(int callingUserId, @NonNull String callingPackage, + @NonNull String packageName, @NonNull String shortcutId, int userId, + @NonNull IntentFilter filter) { + Preconditions.checkStringNotEmpty(callingPackage, "callingPackage"); + Preconditions.checkStringNotEmpty(packageName, "packageName"); + Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); + + return ShortcutService.this.isSharingShortcut(callingUserId, callingPackage, + packageName, shortcutId, userId, filter); + } + private void updateCachedShortcutsInternal(int launcherUserId, @NonNull String callingPackage, @NonNull String packageName, @NonNull List<String> shortcutIds, int userId, boolean doCache) { diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java new file mode 100644 index 000000000000..7977e931c37f --- /dev/null +++ b/services/core/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.soundtrigger_middleware; + +import android.media.ICaptureStateListener; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.concurrent.Semaphore; +import java.util.function.Consumer; + +/** + * This is a never-give-up listener for sound trigger external capture state notifications, as + * published by the audio policy service. + * + * This class will constantly try to connect to the service over a background thread and tolerate + * its death. The client will be notified by a single provided function that is called in a + * synchronized manner. + * For simplicity, there is currently no way to stop the tracker. This is possible to add if the + * need ever arises. + */ +class ExternalCaptureStateTracker { + private static final String TAG = "CaptureStateTracker"; + /** Our client's listener. */ + private final Consumer<Boolean> mListener; + /** This semaphore will get a permit every time we need to reconnect. */ + private final Semaphore mNeedToConnect = new Semaphore(1); + + /** + * Constructor. Will start a background thread to do the work. + * + * @param listener A client provided listener that will be called on state + * changes. May be + * called multiple consecutive times with the same value. Never + * called + * concurrently. + */ + ExternalCaptureStateTracker(Consumer<Boolean> listener) { + mListener = listener; + new Thread(this::run).start(); + } + + /** + * Routine for the background thread. Keeps trying to reconnect. + */ + private void run() { + while (true) { + mNeedToConnect.acquireUninterruptibly(); + connect(); + } + } + + /** + * Connect to the service, install listener and death notifier. + */ + private native void connect(); + + /** + * Called by native code to invoke the client listener. + * + * @param active true when external capture is active. + */ + private void setCaptureState(boolean active) { + mListener.accept(active); + } + + /** + * Called by native code when the remote service died. + */ + private void binderDied() { + Log.w(TAG, "Audio policy service died"); + mNeedToConnect.release(); + } +} diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java new file mode 100644 index 000000000000..5def7621c148 --- /dev/null +++ b/services/core/java/com/android/server/soundtrigger_middleware/ISoundTriggerMiddlewareInternal.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.soundtrigger_middleware; + +import android.media.ICaptureStateListener; +import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; + +/** + * This interface unifies ISoundTriggerMiddlewareService with ICaptureStateListener. + */ +public interface ISoundTriggerMiddlewareInternal extends ISoundTriggerMiddlewareService, + ICaptureStateListener { +} diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java index 9f4b09a62aff..d76b1bf71a1c 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java @@ -50,7 +50,7 @@ import java.util.List; * * @hide */ -public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareService { +public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareInternal { static private final String TAG = "SoundTriggerMiddlewareImpl"; private final SoundTriggerModule[] mModules; @@ -124,7 +124,7 @@ public class SoundTriggerMiddlewareImpl implements ISoundTriggerMiddlewareServic } @Override - public void setExternalCaptureState(boolean active) { + public void setCaptureState(boolean active) { for (SoundTriggerModule module : mModules) { module.setExternalCaptureState(active); } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java index fa78cb0931c2..04ba6bfeb4ee 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java @@ -62,11 +62,11 @@ import java.util.LinkedList; * String, Object, Object[])}, {@link #logVoidReturnWithObject(Object, String, Object[])} and {@link * #logExceptionWithObject(Object, String, Exception, Object[])}. */ -public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareService, Dumpable { +public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable { private static final String TAG = "SoundTriggerMiddlewareLogging"; - private final @NonNull ISoundTriggerMiddlewareService mDelegate; + private final @NonNull ISoundTriggerMiddlewareInternal mDelegate; - public SoundTriggerMiddlewareLogging(@NonNull ISoundTriggerMiddlewareService delegate) { + public SoundTriggerMiddlewareLogging(@NonNull ISoundTriggerMiddlewareInternal delegate) { mDelegate = delegate; } @@ -96,12 +96,12 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareSer } @Override - public void setExternalCaptureState(boolean active) throws RemoteException { + public void setCaptureState(boolean active) throws RemoteException { try { - mDelegate.setExternalCaptureState(active); - logVoidReturn("setExternalCaptureState", active); + mDelegate.setCaptureState(active); + logVoidReturn("setCaptureState", active); } catch (Exception e) { - logException("setExternalCaptureState", e, active); + logException("setCaptureState", e, active); throw e; } } diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java index 0d8fc76e1bd2..929d92f56c44 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java @@ -63,14 +63,21 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic static private final String TAG = "SoundTriggerMiddlewareService"; @NonNull - private final ISoundTriggerMiddlewareService mDelegate; + private final ISoundTriggerMiddlewareInternal mDelegate; /** * Constructor for internal use only. Could be exposed for testing purposes in the future. * Users should access this class via {@link Lifecycle}. */ - private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareService delegate) { + private SoundTriggerMiddlewareService(@NonNull ISoundTriggerMiddlewareInternal delegate) { mDelegate = Objects.requireNonNull(delegate); + new ExternalCaptureStateTracker(active -> { + try { + mDelegate.setCaptureState(active); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + }); } @Override @@ -86,11 +93,6 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic return new ModuleService(mDelegate.attach(handle, callback)); } - @Override - public void setExternalCaptureState(boolean active) throws RemoteException { - mDelegate.setExternalCaptureState(active); - } - @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { if (mDelegate instanceof Dumpable) { ((Dumpable) mDelegate).dump(fout); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java index 06f2d65c13b2..008933f643dd 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java @@ -105,7 +105,7 @@ import java.util.Set; * * {@hide} */ -public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddlewareService, Dumpable { +public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddlewareInternal, Dumpable { private static final String TAG = "SoundTriggerMiddlewareValidation"; private enum ModuleState { @@ -114,12 +114,12 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware DEAD }; - private final @NonNull ISoundTriggerMiddlewareService mDelegate; + private final @NonNull ISoundTriggerMiddlewareInternal mDelegate; private final @NonNull Context mContext; private Map<Integer, Set<ModuleService>> mModules; public SoundTriggerMiddlewareValidation( - @NonNull ISoundTriggerMiddlewareService delegate, @NonNull Context context) { + @NonNull ISoundTriggerMiddlewareInternal delegate, @NonNull Context context) { mDelegate = delegate; mContext = context; } @@ -213,21 +213,15 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware } @Override - public void setExternalCaptureState(boolean active) { - // Permission check. - checkPreemptPermissions(); - // Input validation (always valid). - - // State validation (always valid). - + public void setCaptureState(boolean active) { + // This is an internal call. No permissions needed. + // // Normally, we would acquire a lock here. However, we do not access any state here so it // is safe to not lock. This call is typically done from a different context than all the // other calls and may result in a deadlock if we lock here (between the audio server and // the system server). - - // From here on, every exception isn't client's fault. try { - mDelegate.setExternalCaptureState(active); + mDelegate.setCaptureState(active); } catch (Exception e) { throw handleException(e); } @@ -252,16 +246,6 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware /** * Throws a {@link SecurityException} if caller permanently doesn't have the given permission, * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if - * caller temporarily doesn't have the right permissions to preempt active sound trigger - * sessions. - */ - void checkPreemptPermissions() { - enforcePermission(Manifest.permission.PREEMPT_SOUND_TRIGGER); - } - - /** - * Throws a {@link SecurityException} if caller permanently doesn't have the given permission, - * or a {@link ServiceSpecificException} with a {@link Status#TEMPORARY_PERMISSION_DENIED} if * caller temporarily doesn't have the given permission. * * @param permission The permission to check. @@ -806,4 +790,4 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware } } } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 155b2e09284a..24ab89b027b2 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -29,6 +29,7 @@ import static android.os.storage.VolumeInfo.TYPE_PUBLIC; import static android.util.MathUtils.abs; import static android.util.MathUtils.constrain; +import static com.android.internal.util.FrameworkStatsLog.ANNOTATION_ID_IS_UID; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs; import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs; @@ -750,6 +751,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent.Builder e = StatsEvent.newBuilder(); e.setAtomId(atomTag); e.writeInt(entry.uid); + e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); if (withFgbg) { e.writeInt(entry.set); } @@ -920,6 +922,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(traffic.getUid()) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeLong(traffic.getRxBytes()) .writeLong(traffic.getTxBytes()) .build(); @@ -1006,6 +1009,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeLong(userTimeUs) .writeLong(systemTimeUs) .build(); @@ -1036,6 +1040,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeInt(freqIndex) .writeLong(cpuFreqTimeMs[freqIndex]) .build(); @@ -1066,6 +1071,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeLong(cpuActiveTimesMs) .build(); pulledData.add(e); @@ -1094,6 +1100,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeInt(i) .writeLong(cpuClusterTimesMs[i]) .build(); @@ -1289,6 +1296,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(processMemoryState.uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(processMemoryState.processName) .writeInt(processMemoryState.oomScore) .writeLong(memoryStat.pgfault) @@ -1331,6 +1339,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(managedProcess.uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(managedProcess.processName) // RSS high-water mark in bytes. .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L) @@ -1350,6 +1359,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(snapshot.uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(processCmdlines.valueAt(i)) // RSS high-water mark in bytes. .writeLong(snapshot.rssHighWaterMarkInKilobytes * 1024L) @@ -1384,6 +1394,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(managedProcess.uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(managedProcess.processName) .writeInt(managedProcess.pid) .writeInt(managedProcess.oomScore) @@ -1409,6 +1420,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(snapshot.uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(processCmdlines.valueAt(i)) .writeInt(pid) .writeInt(-1001) // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1. @@ -1481,6 +1493,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(getUidForPid(allocations.pid)) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(readCmdlineFromProcfs(allocations.pid)) .writeInt((int) (allocations.totalSizeInBytes / 1024)) .writeInt(allocations.count) @@ -1593,6 +1606,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(callStat.workSourceUid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(callStat.className) .writeString(callStat.methodName) .writeLong(callStat.callCount) @@ -1669,6 +1683,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(entry.workSourceUid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(entry.handlerClassName) .writeString(entry.threadName) .writeString(entry.messageName) @@ -2112,6 +2127,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeLong(fgCharsRead) .writeLong(fgCharsWrite) .writeLong(fgBytesRead) @@ -2177,6 +2193,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(st.uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(st.name) .writeLong(st.base_utime) .writeLong(st.base_stime) @@ -2235,6 +2252,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent.Builder e = StatsEvent.newBuilder(); e.setAtomId(atomTag); e.writeInt(processCpuUsage.uid); + e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); e.writeInt(processCpuUsage.processId); e.writeInt(threadCpuUsage.threadId); e.writeString(processCpuUsage.processName); @@ -2326,6 +2344,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(bs.uidObj.getUid()) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeLong(milliAmpHrsToNanoAmpSecs(bs.totalPowerMah)) .build(); pulledData.add(e); @@ -2530,6 +2549,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(pkg.applicationInfo.uid) + .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true) .writeString(holderName) .writeString(roleName) .build(); @@ -2613,6 +2633,7 @@ public class StatsPullAtomService extends SystemService { e.setAtomId(atomTag); e.writeString(permName); e.writeInt(pkg.applicationInfo.uid); + e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); if (atomTag == FrameworkStatsLog.DANGEROUS_PERMISSION_STATE) { e.writeString(""); } @@ -2967,6 +2988,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent.Builder e = StatsEvent.newBuilder(); e.setAtomId(atomTag); e.writeInt(uid); + e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); e.writeString(packageName); if (atomTag == FrameworkStatsLog.ATTRIBUTED_APP_OPS) { e.writeString(attributionTag); @@ -3015,6 +3037,7 @@ public class StatsPullAtomService extends SystemService { StatsEvent.Builder e = StatsEvent.newBuilder(); e.setAtomId(atomTag); e.writeInt(message.getUid()); + e.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true); e.writeString(message.getPackageName()); e.writeString(message.getOp()); if (message.getAttributionTag() == null) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 01081404a502..ce7e79714c39 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -503,6 +503,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */ final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>(); + /** Windows whose client's insets states are not up-to-date. */ + final ArrayList<WindowState> mWinInsetsChanged = new ArrayList<>(); + private ScreenRotationAnimation mScreenRotationAnimation; /** @@ -708,7 +711,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } // Sets mBehindIme for each window. Windows behind IME can get IME insets. - w.mBehindIme = mTmpWindowsBehindIme; + if (w.mBehindIme != mTmpWindowsBehindIme) { + w.mBehindIme = mTmpWindowsBehindIme; + mWinInsetsChanged.add(w); + } if (w == mInputMethodWindow) { mTmpWindowsBehindIme = true; } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 9e954f29438c..007af249c580 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -398,7 +398,7 @@ class InsetsPolicy { /** Called on SurfaceAnimationThread without global WM lock held. */ @Override - public void scheduleApplyChangeInsets() { + public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { InsetsState state = getState(); if (mAnimationControl.applyChangeInsets(state)) { mAnimationControl.finish(mAnimatingShown); diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 04454a5b33ec..ba14d48d38ea 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -227,10 +227,19 @@ class InsetsStateController { for (int i = mProviders.size() - 1; i >= 0; i--) { mProviders.valueAt(i).onPostLayout(); } + final ArrayList<WindowState> winInsetsChanged = mDisplayContent.mWinInsetsChanged; if (!mLastState.equals(mState)) { mLastState.set(mState, true /* copySources */); notifyInsetsChanged(); + } else { + // The global insets state has not changed but there might be windows whose conditions + // (e.g., z-order) have changed. They can affect the insets states that we dispatch to + // the clients. + for (int i = winInsetsChanged.size() - 1; i >= 0; i--) { + winInsetsChanged.get(i).notifyInsetsChanged(); + } } + winInsetsChanged.clear(); } void onInsetsModified(InsetsControlTarget windowState, InsetsState state) { diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 74982c6918a2..4c3f73d2d129 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -40,6 +40,7 @@ cc_library_static { "com_android_server_security_VerityUtils.cpp", "com_android_server_SerialService.cpp", "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp", + "com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp", "com_android_server_stats_pull_StatsPullAtomService.cpp", "com_android_server_storage_AppFuseBridge.cpp", "com_android_server_SystemServer.cpp", diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp index e9a5e58e49d1..853eba71d88a 100644 --- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp +++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp @@ -392,6 +392,7 @@ private: mArgs = params.arguments(); mIfs = ifs; mStatusListener = statusListener; + mIfs->setParams({.readLogsEnabled = true}); return true; } bool onStart() final { return true; } @@ -438,7 +439,7 @@ private: } const auto fileId = IncFs_FileIdFromMetadata(file.metadata); - const auto incfsFd(mIfs->openWrite(fileId)); + const base::unique_fd incfsFd(mIfs->openForSpecialOps(fileId).release()); if (incfsFd < 0) { ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. " "Error %d", @@ -716,7 +717,7 @@ private: auto& writeFd = writeFds[fileIdx]; if (writeFd < 0) { - writeFd.reset(this->mIfs->openWrite(fileId)); + writeFd.reset(this->mIfs->openForSpecialOps(fileId).release()); if (writeFd < 0) { ALOGE("Failed to open file %d for writing (%d). Aborting.", header.fileIdx, -writeFd); diff --git a/services/core/jni/com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp b/services/core/jni/com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp new file mode 100644 index 000000000000..ae6cb187fa47 --- /dev/null +++ b/services/core/jni/com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#include <sstream> + +#define LOG_TAG "ExternalCaptureStateTracker" + +#include "core_jni_helpers.h" +#include <log/log.h> +#include <media/AudioSystem.h> + +namespace android { +namespace { + +#define PACKAGE "com/android/server/soundtrigger_middleware" +#define CLASSNAME PACKAGE "/ExternalCaptureStateTracker" + +jclass gExternalCaptureStateTrackerClassId; +jmethodID gSetCaptureStateMethodId; +jmethodID gBinderDiedMethodId; + +void PopulateIds(JNIEnv* env) { + gExternalCaptureStateTrackerClassId = + (jclass) env->NewGlobalRef(FindClassOrDie(env, CLASSNAME)); + gSetCaptureStateMethodId = GetMethodIDOrDie(env, + gExternalCaptureStateTrackerClassId, + "setCaptureState", + "(Z)V"); + gBinderDiedMethodId = GetMethodIDOrDie(env, + gExternalCaptureStateTrackerClassId, + "binderDied", + "()V"); +} + +class Listener : public AudioSystem::CaptureStateListener { +public: + Listener(JNIEnv* env, jobject obj) : mObj(env->NewGlobalRef(obj)) {} + + ~Listener() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mObj); + } + + void onStateChanged(bool active) override { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mObj, gSetCaptureStateMethodId, active); + } + + void onServiceDied() override { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mObj, gBinderDiedMethodId); + } + +private: + jobject mObj; +}; + +void connect(JNIEnv* env, jobject obj) { + sp<AudioSystem::CaptureStateListener> listener(new Listener(env, obj)); + status_t status = + AudioSystem::registerSoundTriggerCaptureStateListener(listener); + LOG_ALWAYS_FATAL_IF(status != NO_ERROR); +} + +const JNINativeMethod gMethods[] = { + {"connect", "()V", reinterpret_cast<void*>(connect)}, +}; + +} // namespace + +int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker( + JNIEnv* env) { + PopulateIds(env); + return RegisterMethodsOrDie(env, + CLASSNAME, + gMethods, + NELEM(gMethods)); +} + +} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index eb486fea0116..b988bd45d786 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -58,6 +58,8 @@ int register_android_server_am_CachedAppOptimizer(JNIEnv* env); int register_android_server_am_LowMemDetector(JNIEnv* env); int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl( JNIEnv* env); +int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker( + JNIEnv* env); int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env); int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); int register_android_server_AdbDebuggingManager(JNIEnv* env); @@ -112,6 +114,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_am_LowMemDetector(env); register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl( env); + register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker( + env); register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env); register_android_server_stats_pull_StatsPullAtomService(env); register_android_server_AdbDebuggingManager(env); diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index 2dbbc5ac6806..97de1800cae2 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -155,6 +155,11 @@ binder::Status BinderIncrementalService::deleteStorage(int32_t storageId) { return ok(); } +binder::Status BinderIncrementalService::setStorageParams(int32_t storage, bool enableReadLogs, int32_t* _aidl_return) { + *_aidl_return = mImpl.setStorageParams(storage, enableReadLogs); + return ok(); +} + binder::Status BinderIncrementalService::makeDirectory(int32_t storageId, const std::string& path, int32_t* _aidl_return) { *_aidl_return = mImpl.makeDir(storageId, path); diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 28613e101b7c..d0357d924586 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -71,6 +71,7 @@ public: binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath, const std::string& libDirRelativePath, const std::string& abi, bool* _aidl_return) final; + binder::Status setStorageParams(int32_t storage, bool enableReadLogs, int32_t* _aidl_return) final; private: android::incremental::IncrementalService mImpl; diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 25da8fe4a2e8..5e3c337da11e 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -563,6 +563,36 @@ StorageId IncrementalService::findStorageId(std::string_view path) const { return it->second->second.storage; } +int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLogs) { + const auto ifs = getIfs(storageId); + if (!ifs) { + return -EINVAL; + } + + using unique_fd = ::android::base::unique_fd; + ::android::os::incremental::IncrementalFileSystemControlParcel control; + control.cmd.reset(unique_fd(dup(ifs->control.cmd()))); + control.pendingReads.reset(unique_fd(dup(ifs->control.pendingReads()))); + auto logsFd = ifs->control.logs(); + if (logsFd >= 0) { + control.log.reset(unique_fd(dup(logsFd))); + } + + std::lock_guard l(mMountOperationLock); + const auto status = mVold->setIncFsMountOptions(control, enableReadLogs); + if (!status.isOk()) { + LOG(ERROR) << "Calling Vold::setIncFsMountOptions() failed: " << status.toString8(); + return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC + ? status.serviceSpecificErrorCode() > 0 ? -status.serviceSpecificErrorCode() + : status.serviceSpecificErrorCode() == 0 + ? -EFAULT + : status.serviceSpecificErrorCode() + : -EIO; + } + + return 0; +} + void IncrementalService::deleteStorage(StorageId storageId) { const auto ifs = getIfs(storageId); if (!ifs) { @@ -737,10 +767,12 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m if (auto ifs = getIfs(storage)) { std::string normPath = normalizePathToStorage(ifs, storage, path); if (normPath.empty()) { + LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path; return -EINVAL; } auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); if (err) { + LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err; return err; } std::vector<uint8_t> metadataBytes; @@ -1182,8 +1214,8 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_ success = false; break; } - android::base::unique_fd writeFd(mIncFs->openWrite(ifs->control, libFileId)); - if (writeFd < 0) { + const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId); + if (!writeFd.ok()) { LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd; success = false; break; diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 406b32e51044..90d58a7adcf0 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -111,6 +111,8 @@ public: int unbind(StorageId storage, std::string_view target); void deleteStorage(StorageId storage); + int setStorageParams(StorageId storage, bool enableReadLogs); + int makeFile(StorageId storage, std::string_view path, int mode, FileId id, incfs::NewFileParams params); int makeDir(StorageId storage, std::string_view path, int mode = 0755); diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index c70a47d40c4e..c3300305f515 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -49,6 +49,7 @@ public: virtual binder::Status unmountIncFs(const std::string& dir) const = 0; virtual binder::Status bindMount(const std::string& sourceDir, const std::string& targetDir) const = 0; + virtual binder::Status setIncFsMountOptions(const ::android::os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs) const = 0; }; class DataLoaderManagerWrapper { @@ -76,7 +77,7 @@ public: virtual ErrorCode link(const Control& control, std::string_view from, std::string_view to) const = 0; virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0; - virtual base::unique_fd openWrite(const Control& control, FileId id) const = 0; + virtual base::unique_fd openForSpecialOps(const Control& control, FileId id) const = 0; virtual ErrorCode writeBlocks(Span<const DataBlock> blocks) const = 0; }; @@ -106,6 +107,9 @@ public: const std::string& targetDir) const override { return mInterface->bindMount(sourceDir, targetDir); } + binder::Status setIncFsMountOptions(const ::android::os::incremental::IncrementalFileSystemControlParcel& control, bool enableReadLogs) const override { + return mInterface->setIncFsMountOptions(control, enableReadLogs); + } private: sp<os::IVold> mInterface; @@ -177,8 +181,8 @@ public: ErrorCode unlink(const Control& control, std::string_view path) const override { return incfs::unlink(control, path); } - base::unique_fd openWrite(const Control& control, FileId id) const override { - return base::unique_fd{incfs::openWrite(control, id)}; + base::unique_fd openForSpecialOps(const Control& control, FileId id) const override { + return base::unique_fd{incfs::openForSpecialOps(control, id).release()}; } ErrorCode writeBlocks(Span<const DataBlock> blocks) const override { return incfs::writeBlocks(blocks); diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index c4b4d1746cbe..cde38fbb3ca2 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -52,6 +52,8 @@ public: MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir)); MOCK_CONST_METHOD2(bindMount, binder::Status(const std::string& sourceDir, const std::string& argetDir)); + MOCK_CONST_METHOD2(setIncFsMountOptions, + binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&, bool)); void mountIncFsFails() { ON_CALL(*this, mountIncFs(_, _, _, _)) @@ -74,6 +76,14 @@ public: void bindMountSuccess() { ON_CALL(*this, bindMount(_, _)).WillByDefault(Return(binder::Status::ok())); } + void setIncFsMountOptionsFails() const { + ON_CALL(*this, setIncFsMountOptions(_, _)) + .WillByDefault( + Return(binder::Status::fromExceptionCode(1, String8("failed to set options")))); + } + void setIncFsMountOptionsSuccess() { + ON_CALL(*this, setIncFsMountOptions(_, _)).WillByDefault(Return(binder::Status::ok())); + } binder::Status getInvalidControlParcel(const std::string& imagePath, const std::string& targetDir, int32_t flags, IncrementalFileSystemControlParcel* _aidl_return) { @@ -175,7 +185,7 @@ public: MOCK_CONST_METHOD3(link, ErrorCode(const Control& control, std::string_view from, std::string_view to)); MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path)); - MOCK_CONST_METHOD2(openWrite, base::unique_fd(const Control& control, FileId id)); + MOCK_CONST_METHOD2(openForSpecialOps, base::unique_fd(const Control& control, FileId id)); MOCK_CONST_METHOD1(writeBlocks, ErrorCode(Span<const DataBlock> blocks)); void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); } @@ -390,6 +400,42 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderSuccess) { ASSERT_TRUE(mIncrementalService->startLoading(storageId)); } +TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) { + mVold->mountIncFsSuccess(); + mIncFs->makeFileSuccess(); + mVold->bindMountSuccess(); + mVold->setIncFsMountOptionsSuccess(); + mDataLoaderManager->initializeDataLoaderSuccess(); + mDataLoaderManager->getDataLoaderSuccess(); + EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + EXPECT_CALL(*mVold, setIncFsMountOptions(_, _)); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + ASSERT_GE(mIncrementalService->setStorageParams(storageId, true), 0); +} + +TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) { + mVold->mountIncFsSuccess(); + mIncFs->makeFileSuccess(); + mVold->bindMountSuccess(); + mVold->setIncFsMountOptionsFails(); + mDataLoaderManager->initializeDataLoaderSuccess(); + mDataLoaderManager->getDataLoaderSuccess(); + EXPECT_CALL(*mDataLoaderManager, destroyDataLoader(_)); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + EXPECT_CALL(*mVold, setIncFsMountOptions(_, _)); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + ASSERT_LT(mIncrementalService->setStorageParams(storageId, true), 0); +} + TEST_F(IncrementalServiceTest, testMakeDirectory) { mVold->mountIncFsSuccess(); mIncFs->makeFileSuccess(); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 2cbb6d5c5bd6..06b344b3b94f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -8595,6 +8595,56 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } } + public void testIsSharingShortcut() throws IntentFilter.MalformedMimeTypeException { + addManifestShortcutResource( + new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), + R.xml.shortcut_share_targets); + updatePackageVersion(CALLING_PACKAGE_1, 1); + mService.mPackageMonitor.onReceive(getTestContext(), + genPackageAddIntent(CALLING_PACKAGE_1, USER_0)); + + setCaller(CALLING_PACKAGE_1, USER_0); + + final ShortcutInfo s1 = makeShortcutWithCategory("s1", + set("com.test.category.CATEGORY1", "com.test.category.CATEGORY2")); + final ShortcutInfo s2 = makeShortcutWithCategory("s2", + set("com.test.category.CATEGORY5", "com.test.category.CATEGORY6")); + final ShortcutInfo s3 = makeShortcut("s3"); + + assertTrue(mManager.setDynamicShortcuts(list(s1, s2, s3))); + assertShortcutIds(assertAllNotKeyFieldsOnly(mManager.getDynamicShortcuts()), + "s1", "s2", "s3"); + + IntentFilter filter_cat1 = new IntentFilter(); + filter_cat1.addDataType("text/plain"); + IntentFilter filter_cat5 = new IntentFilter(); + filter_cat5.addDataType("video/*"); + IntentFilter filter_any = new IntentFilter(); + filter_any.addDataType("*/*"); + + setCaller(LAUNCHER_1, USER_0); + mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS); + + assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s1", USER_0, + filter_cat1)); + assertFalse(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s1", USER_0, + filter_cat5)); + assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s1", USER_0, + filter_any)); + + assertFalse(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s2", USER_0, + filter_cat1)); + assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s2", USER_0, + filter_cat5)); + assertTrue(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s2", USER_0, + filter_any)); + + assertFalse(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s3", USER_0, + filter_any)); + assertFalse(mInternal.isSharingShortcut(USER_0, LAUNCHER_1, CALLING_PACKAGE_1, "s4", USER_0, + filter_any)); + } + private Uri getFileUriFromResource(String fileName, int resId) throws IOException { File file = new File(getTestContext().getFilesDir(), fileName); // Make sure we are not leaving phantom files behind. diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java index 06b5fe48c4cf..ebcf10dd019f 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java @@ -1106,7 +1106,7 @@ public class SoundTriggerMiddlewareImplTest { public void testAbortRecognition() throws Exception { // Make sure the HAL doesn't support concurrent capture. initService(false); - mService.setExternalCaptureState(false); + mService.setCaptureState(false); ISoundTriggerCallback callback = createCallbackMock(); ISoundTriggerModule module = mService.attach(0, callback); @@ -1120,7 +1120,7 @@ public class SoundTriggerMiddlewareImplTest { startRecognition(module, handle, hwHandle); // Abort. - mService.setExternalCaptureState(true); + mService.setCaptureState(true); ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( RecognitionEvent.class); @@ -1142,7 +1142,7 @@ public class SoundTriggerMiddlewareImplTest { verifyNotStartRecognition(); // Now enable it and make sure we are notified. - mService.setExternalCaptureState(false); + mService.setCaptureState(false); verify(callback).onRecognitionAvailabilityChange(true); // Unload the model. @@ -1154,7 +1154,7 @@ public class SoundTriggerMiddlewareImplTest { public void testAbortPhraseRecognition() throws Exception { // Make sure the HAL doesn't support concurrent capture. initService(false); - mService.setExternalCaptureState(false); + mService.setCaptureState(false); ISoundTriggerCallback callback = createCallbackMock(); ISoundTriggerModule module = mService.attach(0, callback); @@ -1168,7 +1168,7 @@ public class SoundTriggerMiddlewareImplTest { startRecognition(module, handle, hwHandle); // Abort. - mService.setExternalCaptureState(true); + mService.setCaptureState(true); ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( PhraseRecognitionEvent.class); @@ -1190,7 +1190,7 @@ public class SoundTriggerMiddlewareImplTest { verifyNotStartRecognition(); // Now enable it and make sure we are notified. - mService.setExternalCaptureState(false); + mService.setCaptureState(false); verify(callback).onRecognitionAvailabilityChange(true); // Unload the model. @@ -1216,7 +1216,7 @@ public class SoundTriggerMiddlewareImplTest { startRecognition(module, handle, hwHandle); // Signal concurrent capture. Shouldn't abort. - mService.setExternalCaptureState(true); + mService.setCaptureState(true); verify(callback, never()).onRecognition(anyInt(), any()); verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean()); @@ -1252,7 +1252,7 @@ public class SoundTriggerMiddlewareImplTest { startRecognition(module, handle, hwHandle); // Signal concurrent capture. Shouldn't abort. - mService.setExternalCaptureState(true); + mService.setCaptureState(true); verify(callback, never()).onPhraseRecognition(anyInt(), any()); verify(callback, never()).onRecognitionAvailabilityChange(anyBoolean()); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 08e492a7b0ff..8b91c7e5d5f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -73,7 +73,6 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testKeyguardOverride() { mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); @@ -81,7 +80,6 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testKeyguardKeep() { mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); mWm.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false /* alwaysKeepCurrent */); @@ -89,7 +87,6 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testForceOverride() { mWm.prepareAppTransition(TRANSIT_KEYGUARD_UNOCCLUDE, false /* alwaysKeepCurrent */); mDc.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, @@ -105,7 +102,6 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testKeepKeyguard_withCrashing() { mWm.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */); mWm.prepareAppTransition(TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); @@ -113,7 +109,6 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testAppTransitionStateForMultiDisplay() { // Create 2 displays & presume both display the state is ON for ready to display & animate. final DisplayContent dc1 = createNewDisplay(Display.STATE_ON); @@ -182,7 +177,6 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testLoadAnimationSafely() { DisplayContent dc = createNewDisplay(Display.STATE_ON); assertNull(dc.mAppTransition.loadAnimationSafely( diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java index 6e78a271458a..b93a8fcc115b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java @@ -67,7 +67,6 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void clipAfterAnim_boundsLayerIsCreated() { mActivity.mNeedsAnimationBoundsLayer = true; @@ -91,7 +90,6 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void clipAfterAnim_boundsLayerIsDestroyed() { mActivity.mNeedsAnimationBoundsLayer = true; mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, @@ -126,7 +124,6 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void clipNoneAnim_boundsLayerIsNotCreated() { mActivity.mNeedsAnimationBoundsLayer = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 7928e7602df3..28ae36abbb5b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -677,7 +677,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test - @FlakyTest(bugId = 149760800) public void layoutWindowLw_withLongEdgeDisplayCutout() { addLongEdgeDisplayCutout(); @@ -698,7 +697,6 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test - @FlakyTest(bugId = 149760800) public void layoutWindowLw_withLongEdgeDisplayCutout_never() { addLongEdgeDisplayCutout(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index c370d6c7c516..d0fd50dc497b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -277,7 +277,6 @@ public class DisplayPolicyTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testOverlappingWithNavBar() { final WindowState targetWin = createApplicationWindow(); final WindowFrames winFrame = targetWin.getWindowFrames(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java index 956c200022e9..0eee3ca53c7d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java @@ -147,19 +147,16 @@ public class DragDropControllerTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testDragFlow() { dragFlow(0, ClipData.newPlainText("label", "Test"), 0, 0); } @Test - @FlakyTest(bugId = 131005232) public void testPerformDrag_NullDataWithGrantUri() { dragFlow(View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ, null, 0, 0); } @Test - @FlakyTest(bugId = 131005232) public void testPerformDrag_NullDataToOtherUser() { final WindowState otherUsersWindow = createDropTargetWindow("Other user's window", 1 * UserHandle.PER_USER_RANGE); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index b21ea796396c..89bc65b5a44d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -58,7 +58,6 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Presubmit @RunWith(WindowTestRunner.class) public class InsetsPolicyTest extends WindowTestsBase { diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index db7bce4c8753..61b74b0c1d0f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -32,6 +32,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -49,7 +53,6 @@ import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Presubmit @RunWith(WindowTestRunner.class) public class InsetsStateControllerTest extends WindowTestsBase { @@ -68,7 +71,6 @@ public class InsetsStateControllerTest extends WindowTestsBase { } @Test - @FlakyTest(bugId = 131005232) public void testStripForDispatch_notOwn() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); @@ -102,7 +104,8 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); - assertEquals(0, getController().getInsetsForDispatch(navBar).getSourcesCount()); + assertNull(getController().getInsetsForDispatch(navBar).peekSource(ITYPE_IME)); + assertNull(getController().getInsetsForDispatch(navBar).peekSource(ITYPE_STATUS_BAR)); } @Test @@ -169,6 +172,45 @@ public class InsetsStateControllerTest extends WindowTestsBase { } @Test + public void testStripForDispatch_imeOrderChanged() { + getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null); + + // This window can be the IME target while app cannot be the IME target. + createWindow(null, TYPE_APPLICATION, "base"); + + // Send our spy window (app) into the system so that we can detect the invocation. + final WindowState win = createWindow(null, TYPE_APPLICATION, "app"); + final WindowToken parent = win.mToken; + parent.removeChild(win); + final WindowState app = spy(win); + parent.addWindow(app); + + // Adding FLAG_NOT_FOCUSABLE makes app above IME. + app.mAttrs.flags |= FLAG_NOT_FOCUSABLE; + mDisplayContent.computeImeTarget(true); + mDisplayContent.setLayoutNeeded(); + mDisplayContent.applySurfaceChangesTransaction(); + + // app won't get IME insets while above IME. + assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME)); + + // Reset invocation counter. + clearInvocations(app); + + // Removing FLAG_NOT_FOCUSABLE makes app below IME. + app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE; + mDisplayContent.computeImeTarget(true); + mDisplayContent.setLayoutNeeded(); + mDisplayContent.applySurfaceChangesTransaction(); + + // Make sure app got notified. + verify(app, atLeast(1)).notifyInsetsChanged(); + + // app will get IME insets while below IME. + assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME)); + } + + @Test public void testStripForDispatch_childWindow_altFocusable() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); @@ -247,7 +289,6 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertNull(getController().getControlsForDispatch(app)); } - @FlakyTest(bugId = 124088319) @Test public void testControlRevoked_animation() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java index 34ac835ae18d..67aab7ec3fbf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java @@ -146,7 +146,6 @@ public class RemoteAnimationControllerTest extends WindowTestsBase { } @Test - @FlakyTest(bugId = 133372977) public void testTimeout() throws Exception { final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin"); final AnimationAdapter adapter = mController.createRemoteAnimationRecord(win.mActivityRecord, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java index 9fc160229d45..12ed3c28161f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPersisterTest.java @@ -74,7 +74,6 @@ public class TaskPersisterTest { } @Test - @FlakyTest(bugId = 131005232) public void testTaskIdsPersistence() { SparseBooleanArray taskIdsOnFile = new SparseBooleanArray(); for (int i = 0; i < 100; i++) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java index ea52d7d4b189..93dcc9103640 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java @@ -518,7 +518,6 @@ public class TaskPositionerTests extends WindowTestsBase { assertEquals(expected, actual); } - @FlakyTest(bugId = 129492888) @Test public void testFinishingMovingWhenBinderDied() { spyOn(mWm.mTaskPositioningController); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java index ca84932b8f03..75226b7e66f7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -90,7 +90,6 @@ public class TaskPositioningControllerTests extends WindowTestsBase { assertNull(mTarget.getDragWindowHandleLocked()); } - @FlakyTest(bugId = 129507487) @Test public void testFinishPositioningWhenAppRequested() { assertFalse(mTarget.isPositioningLocked()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 31d68a4a8c5b..f76809b06510 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -184,7 +184,6 @@ public class TaskRecordTests extends ActivityTestsBase { /** Ensures that bounds on freeform stacks are not clipped. */ @Test - @FlakyTest(bugId = 137879065) public void testAppBounds_FreeFormBounds() { final Rect freeFormBounds = new Rect(mParentBounds); freeFormBounds.offset(10, 10); @@ -194,7 +193,6 @@ public class TaskRecordTests extends ActivityTestsBase { /** Ensures that fully contained bounds are not clipped. */ @Test - @FlakyTest(bugId = 137879065) public void testAppBounds_ContainedBounds() { final Rect insetBounds = new Rect(mParentBounds); insetBounds.inset(5, 5, 5, 5); @@ -203,7 +201,6 @@ public class TaskRecordTests extends ActivityTestsBase { } @Test - @FlakyTest(bugId = 137879065) public void testFitWithinBounds() { final Rect parentBounds = new Rect(10, 10, 200, 200); DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay(); @@ -243,7 +240,6 @@ public class TaskRecordTests extends ActivityTestsBase { /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */ @Test - @FlakyTest(bugId = 137879065) public void testBoundsOnModeChangeFreeformToFullscreen() { DisplayContent display = mService.mRootWindowContainer.getDefaultDisplay(); ActivityStack stack = new StackBuilder(mRootWindowContainer).setDisplay(display) diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index e95ccab38960..820d3816a6f6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -449,7 +449,6 @@ public class WindowStateTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 74078662) public void testLayoutSeqResetOnReparent() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); app.mLayoutSeq = 1; @@ -508,7 +507,6 @@ public class WindowStateTests extends WindowTestsBase { } @Test - @FlakyTest(bugId = 74078662) public void testDisplayCutoutIsCalculatedRelativeToFrame() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); WindowFrames wf = app.getWindowFrames(); diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java index 47bf14892ccb..6b194550cc11 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java +++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java @@ -103,8 +103,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { @Override public void onIntentStarted(@NonNull Intent intent, long timestampNs) { if (state == State.UNKNOWN) { - logWarningWithStackTrace( - String.format("IntentStarted during UNKNOWN. " + intent)); + logWarningWithStackTrace("IntentStarted during UNKNOWN. " + intent); incAccIntentStartedEvents(); return; } @@ -128,7 +127,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { @Override public void onIntentFailed() { if (state == State.UNKNOWN) { - logWarningWithStackTrace(String.format("onIntentFailed during UNKNOWN.")); + logWarningWithStackTrace("onIntentFailed during UNKNOWN."); decAccIntentStartedEvents(); return; } @@ -147,8 +146,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity, @Temperature int temperature) { if (state == State.UNKNOWN) { - logWarningWithStackTrace( - String.format("onActivityLaunched during UNKNOWN.")); + logWarningWithStackTrace("onActivityLaunched during UNKNOWN."); return; } if (state != State.INTENT_STARTED) { @@ -165,8 +163,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { @Override public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) { if (state == State.UNKNOWN) { - logWarningWithStackTrace( - String.format("onActivityLaunchCancelled during UNKNOWN.")); + logWarningWithStackTrace("onActivityLaunchCancelled during UNKNOWN."); decAccIntentStartedEvents(); return; } @@ -185,8 +182,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity, long timestampNs) { if (state == State.UNKNOWN) { - logWarningWithStackTrace( - String.format("onActivityLaunchFinished during UNKNOWN.")); + logWarningWithStackTrace("onActivityLaunchFinished during UNKNOWN."); decAccIntentStartedEvents(); return; } @@ -206,8 +202,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity, long timestampNs) { if (state == State.UNKNOWN) { - logWarningWithStackTrace( - String.format("onReportFullyDrawn during UNKNOWN.")); + logWarningWithStackTrace("onReportFullyDrawn during UNKNOWN."); return; } if (state == State.INIT) { @@ -237,8 +232,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { private void incAccIntentStartedEvents() { if (accIntentStartedEvents < 0) { - throw new AssertionError( - String.format("The number of unknowns cannot be negative")); + throw new AssertionError("The number of unknowns cannot be negative"); } if (accIntentStartedEvents == 0) { state = State.UNKNOWN; @@ -250,8 +244,7 @@ public class EventSequenceValidator implements ActivityMetricsLaunchObserver { private void decAccIntentStartedEvents() { if (accIntentStartedEvents <= 0) { - throw new AssertionError( - String.format("The number of unknowns cannot be negative")); + throw new AssertionError("The number of unknowns cannot be negative"); } if(accIntentStartedEvents == 1) { state = State.INIT; diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 6fdc13e6a31b..d524299d7ede 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -493,6 +493,7 @@ public interface RILConstants { int RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT = 209; int RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS = 210; int RIL_REQUEST_GET_BARRING_INFO = 211; + int RIL_REQUEST_ENTER_SIM_DEPERSONALIZATION = 212; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index 2b5720a47eb6..b4f0daa025af 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -480,6 +480,8 @@ public class LinkPropertiesTest { assertEquals(1, rmnet0.getLinkAddresses().size()); assertEquals(1, rmnet0.getAllAddresses().size()); assertEquals(1, rmnet0.getAllLinkAddresses().size()); + assertEquals(1, rmnet0.getAllInterfaceNames().size()); + assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); rmnet0.addStackedLink(clat4); assertEquals(1, rmnet0.getStackedLinks().size()); @@ -487,6 +489,9 @@ public class LinkPropertiesTest { assertEquals(1, rmnet0.getLinkAddresses().size()); assertEquals(2, rmnet0.getAllAddresses().size()); assertEquals(2, rmnet0.getAllLinkAddresses().size()); + assertEquals(2, rmnet0.getAllInterfaceNames().size()); + assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); + assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1)); rmnet0.addStackedLink(clat4); assertEquals(1, rmnet0.getStackedLinks().size()); @@ -494,6 +499,9 @@ public class LinkPropertiesTest { assertEquals(1, rmnet0.getLinkAddresses().size()); assertEquals(2, rmnet0.getAllAddresses().size()); assertEquals(2, rmnet0.getAllLinkAddresses().size()); + assertEquals(2, rmnet0.getAllInterfaceNames().size()); + assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); + assertEquals("clat4", rmnet0.getAllInterfaceNames().get(1)); assertEquals(0, clat4.getStackedLinks().size()); @@ -513,6 +521,8 @@ public class LinkPropertiesTest { assertEquals(1, rmnet0.getLinkAddresses().size()); assertEquals(1, rmnet0.getAllAddresses().size()); assertEquals(1, rmnet0.getAllLinkAddresses().size()); + assertEquals(1, rmnet0.getAllInterfaceNames().size()); + assertEquals("rmnet0", rmnet0.getAllInterfaceNames().get(0)); assertFalse(rmnet0.removeStackedLink("clat4")); } @@ -1197,4 +1207,48 @@ public class LinkPropertiesTest { lp.clear(); assertNull(lp.getCaptivePortalData()); } + + private LinkProperties makeIpv4LinkProperties() { + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setInterfaceName(NAME); + linkProperties.addLinkAddress(LINKADDRV4); + linkProperties.addDnsServer(DNS1); + linkProperties.addRoute(new RouteInfo(GATEWAY1)); + linkProperties.addRoute(new RouteInfo(GATEWAY2)); + return linkProperties; + } + + private LinkProperties makeIpv6LinkProperties() { + final LinkProperties linkProperties = new LinkProperties(); + linkProperties.setInterfaceName(NAME); + linkProperties.addLinkAddress(LINKADDRV6); + linkProperties.addDnsServer(DNS6); + linkProperties.addRoute(new RouteInfo(GATEWAY61)); + linkProperties.addRoute(new RouteInfo(GATEWAY62)); + return linkProperties; + } + + @Test + public void testHasIpv4DefaultRoute() { + final LinkProperties Ipv4 = makeIpv4LinkProperties(); + assertTrue(Ipv4.hasIpv4DefaultRoute()); + final LinkProperties Ipv6 = makeIpv6LinkProperties(); + assertFalse(Ipv6.hasIpv4DefaultRoute()); + } + + @Test + public void testHasIpv4DnsServer() { + final LinkProperties Ipv4 = makeIpv4LinkProperties(); + assertTrue(Ipv4.hasIpv4DnsServer()); + final LinkProperties Ipv6 = makeIpv6LinkProperties(); + assertFalse(Ipv6.hasIpv4DnsServer()); + } + + @Test + public void testHasIpv6DnsServer() { + final LinkProperties Ipv4 = makeIpv4LinkProperties(); + assertFalse(Ipv4.hasIpv6DnsServer()); + final LinkProperties Ipv6 = makeIpv6LinkProperties(); + assertTrue(Ipv6.hasIpv6DnsServer()); + } } diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp index 3eabb14e3fd4..54c5b9059fb0 100644 --- a/tools/stats_log_api_gen/java_writer.cpp +++ b/tools/stats_log_api_gen/java_writer.cpp @@ -39,6 +39,15 @@ static int write_java_q_logger_class(FILE* out, const SignatureInfoMap& signatur return 0; } +static void write_java_annotation_constants(FILE* out) { + fprintf(out, " // Annotation constants.\n"); + + for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) { + fprintf(out, " public static final byte %s = %hhu;\n", name.c_str(), id); + } + fprintf(out, "\n"); +} + static void write_annotations(FILE* out, int argIndex, const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) { FieldNumberToAtomDeclSet::const_iterator fieldNumberToAtomDeclSetIt = @@ -48,32 +57,28 @@ static void write_annotations(FILE* out, int argIndex, } const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second; for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) { - fprintf(out, " if (code == %d) {\n", atomDecl->code); + const string atomConstant = make_constant_name(atomDecl->name); + fprintf(out, " if (%s == code) {\n", atomConstant.c_str()); const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex); int resetState = -1; int defaultState = -1; for (const shared_ptr<Annotation>& annotation : annotations) { - // TODO(b/151786433): Write atom constant name instead of atom id literal. + const string& annotationConstant = + ANNOTATION_ID_CONSTANTS.at(annotation->annotationId); switch (annotation->type) { - // TODO(b/151776731): Check for reset state annotation and only include - // reset state when field value == default state annotation value. case ANNOTATION_TYPE_INT: - // TODO(b/151786433): Write annotation constant name instead of - // annotation id literal. if (ANNOTATION_ID_RESET_STATE == annotation->annotationId) { resetState = annotation->value.intValue; } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) { defaultState = annotation->value.intValue; } else { - fprintf(out, " builder.addIntAnnotation((byte) %d, %d);\n", - annotation->annotationId, annotation->value.intValue); + fprintf(out, " builder.addIntAnnotation(%s, %d);\n", + annotationConstant.c_str(), annotation->value.intValue); } break; case ANNOTATION_TYPE_BOOL: - // TODO(b/151786433): Write annotation constant name instead of - // annotation id literal. - fprintf(out, " builder.addBooleanAnnotation((byte) %d, %s);\n", - annotation->annotationId, + fprintf(out, " builder.addBooleanAnnotation(%s, %s);\n", + annotationConstant.c_str(), annotation->value.boolValue ? "true" : "false"); break; default: @@ -81,9 +86,11 @@ static void write_annotations(FILE* out, int argIndex, } } if (defaultState != -1 && resetState != -1) { + const string& annotationConstant = + ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_RESET_STATE); fprintf(out, " if (arg%d == %d) {\n", argIndex, resetState); - fprintf(out, " builder.addIntAnnotation((byte) %d, %d);\n", - ANNOTATION_ID_RESET_STATE, defaultState); + fprintf(out, " builder.addIntAnnotation(%s, %d);\n", + annotationConstant.c_str(), defaultState); fprintf(out, " }\n"); } fprintf(out, " }\n"); @@ -311,6 +318,7 @@ int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attribut write_java_atom_codes(out, atoms); write_java_enum_values(out, atoms); + write_java_annotation_constants(out); int errors = 0; diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp index c0d73fa6261f..d8db62087f8f 100644 --- a/tools/stats_log_api_gen/native_writer.cpp +++ b/tools/stats_log_api_gen/native_writer.cpp @@ -21,6 +21,16 @@ namespace android { namespace stats_log_api_gen { +static void write_native_annotation_constants(FILE* out) { + fprintf(out, "// Annotation constants.\n"); + + for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) { + fprintf(out, "const uint8_t %s = %hhu;\n", name.c_str(), id); + } + fprintf(out, "\n"); +} + + static void write_annotations(FILE* out, int argIndex, const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet, const string& methodPrefix, const string& methodSuffix) { @@ -31,33 +41,31 @@ static void write_annotations(FILE* out, int argIndex, } const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second; for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) { - fprintf(out, " if (code == %d) {\n", atomDecl->code); + const string atomConstant = make_constant_name(atomDecl->name); + fprintf(out, " if (%s == code) {\n", atomConstant.c_str()); const AnnotationSet& annotations = atomDecl->fieldNumberToAnnotations.at(argIndex); int resetState = -1; int defaultState = -1; for (const shared_ptr<Annotation>& annotation : annotations) { - // TODO(b/151786433): Write atom constant name instead of atom id literal. + const string& annotationConstant = + ANNOTATION_ID_CONSTANTS.at(annotation->annotationId); switch (annotation->type) { - // TODO(b/151776731): Check for reset state annotation and only include - // reset state when field value == default state annotation value. case ANNOTATION_TYPE_INT: - // TODO(b/151786433): Write annotation constant name instead of - // annotation id literal. if (ANNOTATION_ID_RESET_STATE == annotation->annotationId) { resetState = annotation->value.intValue; } else if (ANNOTATION_ID_DEFAULT_STATE == annotation->annotationId) { defaultState = annotation->value.intValue; } else { - fprintf(out, " %saddInt32Annotation(%s%d, %d);\n", + fprintf(out, " %saddInt32Annotation(%s%s, %d);\n", methodPrefix.c_str(), methodSuffix.c_str(), - annotation->annotationId, annotation->value.intValue); + annotationConstant.c_str(), annotation->value.intValue); } break; case ANNOTATION_TYPE_BOOL: // TODO(b/151786433): Write annotation constant name instead of // annotation id literal. - fprintf(out, " %saddBoolAnnotation(%s%d, %s);\n", methodPrefix.c_str(), - methodSuffix.c_str(), annotation->annotationId, + fprintf(out, " %saddBoolAnnotation(%s%s, %s);\n", methodPrefix.c_str(), + methodSuffix.c_str(), annotationConstant.c_str(), annotation->value.boolValue ? "true" : "false"); break; default: @@ -65,9 +73,11 @@ static void write_annotations(FILE* out, int argIndex, } } if (defaultState != -1 && resetState != -1) { + const string& annotationConstant = + ANNOTATION_ID_CONSTANTS.at(ANNOTATION_ID_RESET_STATE); fprintf(out, " if (arg%d == %d) {\n", argIndex, resetState); - fprintf(out, " %saddInt32Annotation(%s%d, %d);\n", methodPrefix.c_str(), - methodSuffix.c_str(), ANNOTATION_ID_RESET_STATE, defaultState); + fprintf(out, " %saddInt32Annotation(%s%s, %d);\n", methodPrefix.c_str(), + methodSuffix.c_str(), annotationConstant.c_str(), defaultState); fprintf(out, " }\n"); } fprintf(out, " }\n"); @@ -314,6 +324,8 @@ int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attrib } } + write_native_annotation_constants(out); + fprintf(out, "struct BytesField {\n"); fprintf(out, " BytesField(char const* array, size_t len) : arg(array), " diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h index 57b6f6254562..1f644426ffa9 100644 --- a/tools/stats_log_api_gen/utils.h +++ b/tools/stats_log_api_gen/utils.h @@ -38,6 +38,14 @@ const int JAVA_MODULE_REQUIRES_FLOAT = 0x01; const int JAVA_MODULE_REQUIRES_ATTRIBUTION = 0x02; const int JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS = 0x04; +const map<unsigned char, string> ANNOTATION_ID_CONSTANTS = { + { ANNOTATION_ID_IS_UID, "ANNOTATION_ID_IS_UID" }, + { ANNOTATION_ID_TRUNCATE_TIMESTAMP, "ANNOTATION_ID_TRUNCATE_TIMESTAMP" }, + { ANNOTATION_ID_STATE_OPTION, "ANNOTATION_ID_STATE_OPTION" }, + { ANNOTATION_ID_RESET_STATE, "ANNOTATION_ID_RESET_STATE" }, + { ANNOTATION_ID_STATE_NESTED, "ANNOTATION_ID_STATE_NESTED" } +}; + string make_constant_name(const string& str); const char* cpp_type_name(java_type_t type); |