diff options
193 files changed, 4793 insertions, 1645 deletions
diff --git a/Android.mk b/Android.mk index b3db4097f240..4d4baa9a41fd 100644 --- a/Android.mk +++ b/Android.mk @@ -694,6 +694,7 @@ aidl_files := \ frameworks/base/telephony/java/android/telephony/NeighboringCellInfo.aidl \ frameworks/base/telephony/java/android/telephony/ModemActivityInfo.aidl \ frameworks/base/telephony/java/android/telephony/UiccAccessRule.aidl \ + frameworks/base/telephony/java/android/telephony/data/DataProfile.aidl \ frameworks/base/telephony/java/android/telephony/euicc/DownloadableSubscription.aidl \ frameworks/base/telephony/java/android/telephony/euicc/EuiccInfo.aidl \ frameworks/base/location/java/android/location/Location.aidl \ @@ -913,7 +914,7 @@ packages_to_document := \ # Search through the base framework dirs for these packages. # The result will be relative to frameworks/base. fwbase_dirs_to_document := \ - legacy-test/src \ + test-base/src \ $(patsubst $(LOCAL_PATH)/%,%, \ $(wildcard \ $(foreach dir, $(FRAMEWORKS_BASE_JAVA_SRC_DIRS), \ @@ -1029,6 +1030,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -manifest ./frameworks/base/core/res/AndroidManifest.xml \ -hidePackage com.android.okhttp \ -hidePackage com.android.org.conscrypt \ + -hidePackage com.android.server \ -since $(SRC_API_DIR)/1.xml 1 \ -since $(SRC_API_DIR)/2.xml 2 \ -since $(SRC_API_DIR)/3.xml 3 \ @@ -1056,7 +1058,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -since $(SRC_API_DIR)/25.txt 25 \ -since $(SRC_API_DIR)/26.txt 26 \ -since $(SRC_API_DIR)/27.txt 27 \ - -werror -lerror -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128 \ + -werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \ -overview $(LOCAL_PATH)/core/java/overview.html \ framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \ @@ -1562,7 +1564,8 @@ LOCAL_SRC_FILES := \ tools/streaming_proto/stream.proto \ cmds/am/proto/instrumentation_data.proto \ $(call all-proto-files-under, core/proto) \ - $(call all-proto-files-under, libs/incident/proto) + $(call all-proto-files-under, libs/incident/proto) \ + $(call all-proto-files-under, cmds/statsd/src) include $(BUILD_HOST_JAVA_LIBRARY) # ==== java proto device library (for test only) ============================== diff --git a/api/current.txt b/api/current.txt index c1188dcd330b..9e4e923ba1e5 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6588,6 +6588,7 @@ package android.app.admin { public abstract class NetworkEvent implements android.os.Parcelable { method public int describeContents(); + method public long getId(); method public java.lang.String getPackageName(); method public long getTimestamp(); field public static final android.os.Parcelable.Creator<android.app.admin.NetworkEvent> CREATOR; @@ -37499,6 +37500,7 @@ package android.service.autofill { public final class SaveCallback { method public void onFailure(java.lang.CharSequence); method public void onSuccess(); + method public void onSuccess(android.content.IntentSender); } public final class SaveInfo implements android.os.Parcelable { @@ -37555,6 +37557,7 @@ package android.service.autofill { public final class Validators { method public static android.service.autofill.Validator and(android.service.autofill.Validator...); + method public static android.service.autofill.Validator not(android.service.autofill.Validator); method public static android.service.autofill.Validator or(android.service.autofill.Validator...); } @@ -41768,8 +41771,6 @@ package android.test.suitebuilder { public deprecated class TestSuiteBuilder { ctor public TestSuiteBuilder(java.lang.Class); ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader); - method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>); - method public final android.test.suitebuilder.TestSuiteBuilder addRequirements(com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>...); method public final junit.framework.TestSuite build(); method public android.test.suitebuilder.TestSuiteBuilder excludePackages(java.lang.String...); method protected java.lang.String getSuiteName(); diff --git a/api/removed.txt b/api/removed.txt index 2934846b480e..be4d5be1e1a3 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -210,10 +210,6 @@ package android.media { package android.media.tv { - public final class TvInputManager { - method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo); - } - public class TvView extends android.view.ViewGroup { method public void requestUnblockContent(android.media.tv.TvContentRating); } diff --git a/api/system-current.txt b/api/system-current.txt index 9dca6f782385..95f28e4773cb 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -751,6 +751,20 @@ package android.content.pm { field public java.lang.String credentialProtectedDataDir; } + public final class InstantAppInfo implements android.os.Parcelable { + ctor public InstantAppInfo(android.content.pm.ApplicationInfo, java.lang.String[], java.lang.String[]); + ctor public InstantAppInfo(java.lang.String, java.lang.CharSequence, java.lang.String[], java.lang.String[]); + method public int describeContents(); + method public android.content.pm.ApplicationInfo getApplicationInfo(); + method public java.lang.String[] getGrantedPermissions(); + method public java.lang.String getPackageName(); + method public java.lang.String[] getRequestedPermissions(); + method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager); + method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppInfo> CREATOR; + } + public final class InstantAppIntentFilter implements android.os.Parcelable { ctor public InstantAppIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>); method public int describeContents(); @@ -4131,6 +4145,39 @@ package android.telephony { } +package android.telephony.data { + + public final class DataProfile implements android.os.Parcelable { + ctor public DataProfile(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, int, int, int, int, boolean, int, java.lang.String, int, int, java.lang.String, java.lang.String, boolean); + ctor public DataProfile(android.os.Parcel); + method public int describeContents(); + method public java.lang.String getApn(); + method public int getAuthType(); + method public int getBearerBitmap(); + method public int getMaxConns(); + method public int getMaxConnsTime(); + method public int getMtu(); + method public java.lang.String getMvnoMatchData(); + method public java.lang.String getMvnoType(); + method public java.lang.String getPassword(); + method public int getProfileId(); + method public java.lang.String getProtocol(); + method public java.lang.String getRoamingProtocol(); + method public int getSupportedApnTypesBitmap(); + method public int getType(); + method public java.lang.String getUserName(); + method public int getWaitTime(); + method public boolean isEnabled(); + method public boolean isModemCognitive(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.data.DataProfile> CREATOR; + field public static final int TYPE_3GPP = 1; // 0x1 + field public static final int TYPE_3GPP2 = 2; // 0x2 + field public static final int TYPE_COMMON = 0; // 0x0 + } + +} + package android.telephony.ims { public class ImsService extends android.app.Service { diff --git a/api/system-removed.txt b/api/system-removed.txt index 396bcd640894..f98d011faf77 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -103,6 +103,10 @@ package android.content.pm { package android.media.tv { + public final class TvInputManager { + method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo); + } + public static final class TvInputManager.Hardware { method public boolean dispatchKeyEventToHdmi(android.view.KeyEvent); } @@ -111,6 +115,13 @@ package android.media.tv { package android.net.wifi { + public deprecated class BatchedScanResult implements android.os.Parcelable { + ctor public BatchedScanResult(); + ctor public BatchedScanResult(android.net.wifi.BatchedScanResult); + field public final java.util.List<android.net.wifi.ScanResult> scanResults; + field public boolean truncated; + } + public class ScanResult implements android.os.Parcelable { field public boolean untrusted; } diff --git a/api/test-current.txt b/api/test-current.txt index 2e6c40ac34aa..8647ed397c49 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -75,9 +75,11 @@ package android.app { method public int describeContents(); method public int getActivityType(); method public android.graphics.Rect getAppBounds(); + method public android.graphics.Rect getBounds(); method public int getWindowingMode(); method public void setActivityType(int); method public void setAppBounds(android.graphics.Rect); + method public void setBounds(android.graphics.Rect); method public void setTo(android.app.WindowConfiguration); method public void setWindowingMode(int); method public void writeToParcel(android.os.Parcel, int); diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 4d975fcf9c64..1f15c5e97e0d 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -57,7 +57,8 @@ statsd_common_src := \ src/StatsLogProcessor.cpp \ src/StatsService.cpp \ src/stats_util.cpp \ - src/guardrail/MemoryLeakTrackUtil.cpp + src/guardrail/MemoryLeakTrackUtil.cpp \ + src/guardrail/StatsdStats.cpp statsd_common_c_includes := \ $(LOCAL_PATH)/src \ @@ -167,7 +168,8 @@ LOCAL_SRC_FILES := \ tests/metrics/MaxDurationTracker_test.cpp \ tests/metrics/CountMetricProducer_test.cpp \ tests/metrics/EventMetricProducer_test.cpp \ - tests/metrics/ValueMetricProducer_test.cpp + tests/metrics/ValueMetricProducer_test.cpp \ + tests/guardrail/StatsdStats_test.cpp LOCAL_STATIC_LIBRARIES := \ libgmock diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index b5cb20c274ea..57b4fc1e24b3 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define DEBUG true // STOPSHIP if true #include "Log.h" #include "statslog.h" @@ -21,6 +22,7 @@ #include <dirent.h> #include "StatsLogProcessor.h" #include "android-base/stringprintf.h" +#include "guardrail/StatsdStats.h" #include "metrics/CountMetricProducer.h" #include "stats_util.h" #include "storage/StorageManager.h" @@ -73,15 +75,14 @@ StatsLogProcessor::~StatsLogProcessor() { void StatsLogProcessor::onAnomalyAlarmFired( const uint64_t timestampNs, unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet) { - for (const auto& anomaly : anomalySet) { - for (const auto& itr : mMetricsManagers) { - itr.second->onAnomalyAlarmFired(timestampNs, anomaly); - } + for (const auto& itr : mMetricsManagers) { + itr.second->onAnomalyAlarmFired(timestampNs, anomalySet); } } // TODO: what if statsd service restarts? How do we know what logs are already processed before? void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { + StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC); // pass the event to metrics managers. for (auto& pair : mMetricsManagers) { pair.second->onLogEvent(msg); @@ -106,23 +107,26 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { } void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) { + ALOGD("Updated configuration for key %s", key.ToString().c_str()); + unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(key, config); + auto it = mMetricsManagers.find(key); if (it != mMetricsManagers.end()) { it->second->finish(); + } else if (mMetricsManagers.size() > StatsdStats::kMaxConfigCount) { + ALOGE("Can't accept more configs!"); + return; } - ALOGD("Updated configuration for key %s", key.ToString().c_str()); - - unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config); if (newMetricsManager->isConfigValid()) { mUidMap->OnConfigUpdated(key); newMetricsManager->setAnomalyMonitor(mAnomalyMonitor); mMetricsManagers[key] = std::move(newMetricsManager); // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)}); - ALOGD("StatsdConfig valid"); + VLOG("StatsdConfig valid"); } else { // If there is any error in the config, don't use it. - ALOGD("StatsdConfig NOT valid"); + ALOGE("StatsdConfig NOT valid"); } } @@ -195,6 +199,7 @@ void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outD iter.rp()->move(toRead); } } + StatsdStats::getInstance().noteMetricsReportSent(key); } void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { @@ -204,6 +209,7 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { mMetricsManagers.erase(it); mUidMap->OnConfigRemoved(key); } + StatsdStats::getInstance().noteConfigRemoved(key); std::lock_guard<std::mutex> lock(mBroadcastTimesMutex); mLastBroadcastTimes.erase(key); @@ -223,12 +229,14 @@ void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, } } mLastBroadcastTimes[key] = timestampNs; - ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str()); + VLOG("StatsD requesting broadcast for %s", key.ToString().c_str()); mSendBroadcast(key); + StatsdStats::getInstance().noteBroadcastSent(key); } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data. // We ignore the return value so we force each metric producer to clear its contents. metricsManager->onDumpReport(); - ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str()); + StatsdStats::getInstance().noteDataDropped(key); + VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str()); } } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 57928d07a568..27e3854b91d6 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -44,6 +44,8 @@ public: size_t GetMetricsSize(const ConfigKey& key) const; void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData); + + /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */ void onAnomalyAlarmFired( const uint64_t timestampNs, unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet); diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index ec2650e72c08..618e03d655da 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -22,6 +22,7 @@ #include "config/ConfigKey.h" #include "config/ConfigManager.h" #include "guardrail/MemoryLeakTrackUtil.h" +#include "guardrail/StatsdStats.h" #include "storage/DropboxReader.h" #include "storage/StorageManager.h" @@ -222,7 +223,7 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& } if (!args[0].compare(String8("print-stats"))) { - return cmd_print_stats(out); + return cmd_print_stats(out, args); } if (!args[0].compare(String8("clear-config"))) { @@ -305,8 +306,9 @@ void StatsService::print_cmd_help(FILE* out) { fprintf(out, " NAME The name of the configuration\n"); fprintf(out, "\n"); fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmd stats print-stats\n"); + fprintf(out, "usage: adb shell cmd stats print-stats [reset]\n"); fprintf(out, " Prints some basic stats.\n"); + fprintf(out, " reset: 1 to reset the statsd stats. default=0.\n"); } status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) { @@ -474,12 +476,20 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String } } -status_t StatsService::cmd_print_stats(FILE* out) { +status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) { vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys(); for (const ConfigKey& key : configs) { fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(), mProcessor->GetMetricsSize(key)); } + fprintf(out, "Detailed statsd stats in logcat..."); + StatsdStats& statsdStats = StatsdStats::getInstance(); + bool reset = false; + if (args.size() > 1) { + reset = strtol(args[1].string(), NULL, 10); + } + vector<uint8_t> output; + statsdStats.dumpStats(&output, reset); return NO_ERROR; } @@ -575,11 +585,10 @@ Status StatsService::informAnomalyAlarmFired() { } if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired succeeded"); - // TODO: check through all counters/timers and see if an anomaly has indeed occurred. - uint64_t currentTimeNs = time(nullptr) * NS_PER_SEC; + uint64_t currentTimeSec = time(nullptr); std::unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> anomalySet = - mAnomalyMonitor->onAlarmFired(currentTimeNs); - mProcessor->onAnomalyAlarmFired(currentTimeNs, anomalySet); + mAnomalyMonitor->popSoonerThan(static_cast<uint32_t>(currentTimeSec)); + mProcessor->onAnomalyAlarmFired(currentTimeSec * NS_PER_SEC, anomalySet); return Status::ok(); } diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 6b5c156969e2..a32595a4a7f4 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -134,7 +134,7 @@ private: /** * Prints some basic stats to std out. */ - status_t cmd_print_stats(FILE* out); + status_t cmd_print_stats(FILE* out, const Vector<String8>& args); /** * Print the event log. diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp index da52a9d66494..2b2bcfc18fbf 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp +++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp @@ -72,7 +72,8 @@ void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) { return; } if (DEBUG) ALOGD("Removing alarm with time %u", alarm->timestampSec); - mPq.remove(alarm); + bool wasPresent = mPq.remove(alarm); + if (!wasPresent) return; if (mPq.empty()) { if (DEBUG) ALOGD("Queue is empty. Cancel any alarm."); mRegisteredAlarmTimeSec = 0; @@ -129,11 +130,6 @@ int64_t AnomalyMonitor::secToMs(uint32_t timeSec) { return ((int64_t)timeSec) * 1000; } -unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::onAlarmFired( - uint64_t timestampNs) { - return popSoonerThan(static_cast<uint32_t>(timestampNs)); -} - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h index 0bd5055631b6..e19c46929a19 100644 --- a/cmds/statsd/src/anomaly/AnomalyMonitor.h +++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h @@ -23,7 +23,6 @@ #include <queue> #include <set> -#include <unordered_map> #include <unordered_set> #include <vector> @@ -97,15 +96,6 @@ public: unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> popSoonerThan( uint32_t timestampSec); - // TODO: Function that uses popSoonerThan to get all alarms that have fired, and then - // iterates over all DurationAnomalyTracker, looking for those alarms. When they're found, - // have them declareAnomaly on those alarms. This means that DurationAnomalyTracker - // must be thread-safe (since this is being called on a different thread). There is no - // worry about missing the alarms (due to them being cancelled after this function being called) - // because DurationAnomalyTracker guarantees that it checks for anaomlies when it cancels - // alarms anyway. - // void declareAnomalies(uint32_t timestampSec); - /** * Returns the projected alarm timestamp that is registered with * StatsCompanionService. This may not be equal to the soonest alarm, @@ -115,8 +105,6 @@ public: return mRegisteredAlarmTimeSec; } - unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> onAlarmFired(uint64_t timestampNs); - private: std::mutex mLock; diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp index 0904a04987e7..7bacb441ee48 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp +++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp @@ -19,32 +19,32 @@ #include "AnomalyTracker.h" +#include <android/os/IIncidentManager.h> +#include <android/os/IncidentReportArgs.h> +#include <binder/IServiceManager.h> #include <time.h> namespace android { namespace os { namespace statsd { -AnomalyTracker::AnomalyTracker(const Alert& alert, const int64_t& bucketSizeNs) +// TODO: Separate DurationAnomalyTracker as a separate subclass and let each MetricProducer +// decide and let which one it wants. +// TODO: Get rid of bucketNumbers, and return to the original circular array method. +AnomalyTracker::AnomalyTracker(const Alert& alert) : mAlert(alert), - mBucketSizeNs(bucketSizeNs), - mNumOfPastPackets(mAlert.number_of_buckets() - 1) { + mNumOfPastBuckets(mAlert.number_of_buckets() - 1) { VLOG("AnomalyTracker() called"); if (mAlert.number_of_buckets() <= 0) { - ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets", + ALOGE("Cannot create AnomalyTracker with %lld buckets", (long long)mAlert.number_of_buckets()); return; } - if (mBucketSizeNs <= 0) { - ALOGE("Cannot create DiscreteAnomalyTracker with bucket size %lld ", - (long long)mBucketSizeNs); - return; - } if (!mAlert.has_trigger_if_sum_gt()) { - ALOGE("Cannot create DiscreteAnomalyTracker without threshold"); + ALOGE("Cannot create AnomalyTracker without threshold"); return; } - reset(); // initialization + resetStorage(); // initialization } AnomalyTracker::~AnomalyTracker() { @@ -52,37 +52,36 @@ AnomalyTracker::~AnomalyTracker() { stopAllAlarms(); } -void AnomalyTracker::reset() { - VLOG("reset() called."); - stopAllAlarms(); +void AnomalyTracker::resetStorage() { + VLOG("resetStorage() called."); mPastBuckets.clear(); // Excludes the current bucket. - mPastBuckets.resize(mNumOfPastPackets); + mPastBuckets.resize(mNumOfPastBuckets); mSumOverPastBuckets.clear(); - mMostRecentBucketNum = -1; - mLastAlarmTimestampNs = -1; + + if (!mAlarms.empty()) VLOG("AnomalyTracker.resetStorage() called but mAlarms is NOT empty!"); } size_t AnomalyTracker::index(int64_t bucketNum) const { - return bucketNum % mNumOfPastPackets; + return bucketNum % mNumOfPastBuckets; } void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) { VLOG("addPastBucket() called."); - if (latestPastBucketNum <= mMostRecentBucketNum - mNumOfPastPackets) { + if (latestPastBucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) { ALOGE("Cannot add a past bucket %lld units in past", (long long)latestPastBucketNum); return; } // The past packets are ancient. Empty out old mPastBuckets[i] values and reset // mSumOverPastBuckets. - if (latestPastBucketNum - mMostRecentBucketNum >= mNumOfPastPackets) { + if (latestPastBucketNum - mMostRecentBucketNum >= mNumOfPastBuckets) { mPastBuckets.clear(); - mPastBuckets.resize(mNumOfPastPackets); + mPastBuckets.resize(mNumOfPastBuckets); mSumOverPastBuckets.clear(); } else { - for (int64_t i = std::max(0LL, (long long)(mMostRecentBucketNum - mNumOfPastPackets + 1)); - i <= latestPastBucketNum - mNumOfPastPackets; i++) { + for (int64_t i = std::max(0LL, (long long)(mMostRecentBucketNum - mNumOfPastBuckets + 1)); + i <= latestPastBucketNum - mNumOfPastBuckets; i++) { const int idx = index(i); subtractBucketFromSum(mPastBuckets[idx]); mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket. @@ -91,7 +90,7 @@ void AnomalyTracker::flushPastBuckets(const int64_t& latestPastBucketNum) { // It is an update operation. if (latestPastBucketNum <= mMostRecentBucketNum && - latestPastBucketNum > mMostRecentBucketNum - mNumOfPastPackets) { + latestPastBucketNum > mMostRecentBucketNum - mNumOfPastBuckets) { subtractBucketFromSum(mPastBuckets[index(latestPastBucketNum)]); } } @@ -190,61 +189,79 @@ bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const Hashab if (currentBucketNum > mMostRecentBucketNum + 1) { addPastBucket(key, 0, currentBucketNum - 1); } - return getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); + return mAlert.has_trigger_if_sum_gt() + && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); } -void AnomalyTracker::declareAnomaly(const uint64_t& timestamp) { - if (mLastAlarmTimestampNs >= 0 && - timestamp - mLastAlarmTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC) { - VLOG("Skipping anomaly check since within refractory period"); +void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { + // TODO: This should also take in the const HashableDimensionKey& key, to pass + // more details to incidentd and to make mRefractoryPeriodEndsSec key-specific. + // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now. + if (isInRefractoryPeriod(timestampNs)) { + VLOG("Skipping anomaly declaration since within refractory period"); return; } // TODO(guardrail): Consider guarding against too short refractory periods. - mLastAlarmTimestampNs = timestamp; + mLastAlarmTimestampNs = timestampNs; + + + // TODO: If we had access to the bucket_size_millis, consider calling resetStorage() + // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) { resetStorage(); } if (mAlert.has_incidentd_details()) { - // TODO: Can construct a name based on the criteria (and/or relay the criteria). - ALOGW("An anomaly (nameless) has occurred! Informing incidentd."); - // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd + if (mAlert.has_name()) { + ALOGW("An anomaly (%s) has occurred! Informing incidentd.", + mAlert.name().c_str()); + } else { + // TODO: Can construct a name based on the criteria (and/or relay the criteria). + ALOGW("An anomaly (nameless) has occurred! Informing incidentd."); + } + informIncidentd(); } else { ALOGW("An anomaly has occurred! (But informing incidentd not requested.)"); } } void AnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey, - const uint64_t& timestamp) { + const uint64_t& timestampNs) { auto itr = mAlarms.find(dimensionKey); if (itr == mAlarms.end()) { return; } if (itr->second != nullptr && - static_cast<uint32_t>(timestamp / NS_PER_SEC) >= itr->second->timestampSec) { - declareAnomaly(timestamp); + static_cast<uint32_t>(timestampNs / NS_PER_SEC) >= itr->second->timestampSec) { + declareAnomaly(timestampNs); stopAlarm(dimensionKey); } } -void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestamp, +void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const HashableDimensionKey& key, const int64_t& currentBucketValue) { if (detectAnomaly(currBucketNum, key, currentBucketValue)) { - declareAnomaly(timestamp); + declareAnomaly(timestampNs); } } -void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestamp, +void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const DimToValMap& currentBucket) { if (detectAnomaly(currBucketNum, currentBucket)) { - declareAnomaly(timestamp); + declareAnomaly(timestampNs); } } void AnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey, - const uint64_t& timestamp) { - sp<const AnomalyAlarm> alarm = new AnomalyAlarm{static_cast<uint32_t>(timestamp / NS_PER_SEC)}; + const uint64_t& timestampNs) { + uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC); + if (isInRefractoryPeriod(timestampNs)) { + VLOG("Skipping setting anomaly alarm since it'd fall in the refractory period"); + return; + } + + sp<const AnomalyAlarm> alarm = new AnomalyAlarm{timestampSec}; mAlarms.insert({dimensionKey, alarm}); if (mAnomalyMonitor != nullptr) { mAnomalyMonitor->add(alarm); @@ -255,9 +272,9 @@ void AnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) { auto itr = mAlarms.find(dimensionKey); if (itr != mAlarms.end()) { mAlarms.erase(dimensionKey); - } - if (mAnomalyMonitor != nullptr) { - mAnomalyMonitor->remove(itr->second); + if (mAnomalyMonitor != nullptr) { + mAnomalyMonitor->remove(itr->second); + } } } @@ -271,6 +288,58 @@ void AnomalyTracker::stopAllAlarms() { } } +bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs) { + return mLastAlarmTimestampNs >= 0 && + timestampNs - mLastAlarmTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC; +} + +void AnomalyTracker::informAlarmsFired(const uint64_t& timestampNs, + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms) { + + if (firedAlarms.empty() || mAlarms.empty()) return; + // Find the intersection of firedAlarms and mAlarms. + // The for loop is inefficient, since it loops over all keys, but that's okay since it is very + // seldomly called. The alternative would be having AnomalyAlarms store information about the + // AnomalyTracker and key, but that's a lot of data overhead to speed up something that is + // rarely ever called. + unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> matchedAlarms; + for (const auto& kv : mAlarms) { + if (firedAlarms.count(kv.second) > 0) { + matchedAlarms.insert({kv.first, kv.second}); + } + } + + // Now declare each of these alarms to have fired. + for (const auto& kv : matchedAlarms) { + declareAnomaly(timestampNs /* TODO: , kv.first */); + mAlarms.erase(kv.first); + firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. + } +} + +void AnomalyTracker::informIncidentd() { + VLOG("informIncidentd called."); + if (!mAlert.has_incidentd_details()) { + ALOGE("Attempted to call incidentd without any incidentd_details."); + return; + } + sp<IIncidentManager> service = interface_cast<IIncidentManager>( + defaultServiceManager()->getService(android::String16("incident"))); + if (service == NULL) { + ALOGW("Couldn't get the incident service."); + return; + } + + IncidentReportArgs incidentReport; + const Alert::IncidentdDetails& details = mAlert.incidentd_details(); + for (int i = 0; i < details.section_size(); i++) { + incidentReport.addSection(details.section(i)); + } + // TODO: Pass in mAlert.name() into the addHeader? + + service->reportIncident(incidentReport); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h index ce6c99518cda..49e83235f73b 100644 --- a/cmds/statsd/src/anomaly/AnomalyTracker.h +++ b/cmds/statsd/src/anomaly/AnomalyTracker.h @@ -32,10 +32,10 @@ namespace statsd { using std::unordered_map; using std::shared_ptr; -// This anomaly track assmues that all values are non-negative. +// Does NOT allow negative values. class AnomalyTracker : public virtual RefBase { public: - AnomalyTracker(const Alert& alert, const int64_t& bucketSizeNs); + AnomalyTracker(const Alert& alert); virtual ~AnomalyTracker(); @@ -51,12 +51,12 @@ public: const int64_t& currentBucketValue); // Informs incidentd about the detected alert. - void declareAnomaly(const uint64_t& timestamp); + void declareAnomaly(const uint64_t& timestampNs); // Detects the alert and informs the incidentd when applicable. - void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum, + void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const DimToValMap& currentBucket); - void detectAndDeclareAnomaly(const uint64_t& timestamp, const int64_t& currBucketNum, + void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const HashableDimensionKey& key, const int64_t& currentBucketValue); @@ -75,7 +75,7 @@ public: // Declares the anomaly when the alarm expired given the current timestamp. void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey, - const uint64_t& timestamp); + const uint64_t& timestampNs); // Helper function to return the sum value of past buckets at given dimension. int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const; @@ -93,20 +93,26 @@ public: return mLastAlarmTimestampNs; } - inline int getNumOfPastPackets() const { - return mNumOfPastPackets; + inline int getNumOfPastBuckets() const { + return mNumOfPastBuckets; } + // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker, + // and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor. + // TODO: This will actually be called from a different thread, so make it thread-safe! + // TODO: Consider having AnomalyMonitor have a reference to each relevant MetricProducer + // instead of calling it from a chain starting at StatsLogProcessor. + void informAlarmsFired(const uint64_t& timestampNs, + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& firedAlarms); + protected: void flushPastBuckets(const int64_t& currBucketNum); // statsd_config.proto Alert message that defines this tracker. const Alert mAlert; - // Bucket duration in ns. - int64_t mBucketSizeNs = 0; - - // The number of past packets to track in the anomaly detection. - int mNumOfPastPackets = 0; + // Number of past buckets. One less than the total number of buckets needed + // for the anomaly detection (since the current bucket is not in the past). + int mNumOfPastBuckets; // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they // are still active. @@ -134,11 +140,16 @@ protected: // and remove any items with value 0. void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket); + bool isInRefractoryPeriod(const uint64_t& timestampNs); + // Calculates the corresponding bucket index within the circular array. size_t index(int64_t bucketNum) const; - // Resets all data. For use when all the data gets stale. - void reset(); + // Resets all bucket data. For use when all the data gets stale. + void resetStorage(); + + // Informs the incident service that an anomaly has occurred. + void informIncidentd(); FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); diff --git a/cmds/statsd/src/anomaly/indexed_priority_queue.h b/cmds/statsd/src/anomaly/indexed_priority_queue.h index 4982d4b90b7d..99882d0337b1 100644 --- a/cmds/statsd/src/anomaly/indexed_priority_queue.h +++ b/cmds/statsd/src/anomaly/indexed_priority_queue.h @@ -37,7 +37,7 @@ struct SpHash { /** * Min priority queue for generic type AA. * Unlike a regular priority queue, this class is also capable of removing interior elements. - * @tparam Comparator must implement [bool operator()(sp< AA> a, sp< AA> b)], returning + * @tparam Comparator must implement [bool operator()(sp<const AA> a, sp<const AA> b)], returning * whether a should be closer to the top of the queue than b. */ template <class AA, class Comparator> @@ -46,8 +46,11 @@ public: indexed_priority_queue(); /** Adds a into the priority queue. If already present or a==nullptr, does nothing. */ void push(sp<const AA> a); - /** Removes a from the priority queue. If not present or a==nullptr, does nothing. */ - void remove(sp<const AA> a); + /* + * Removes a from the priority queue. If not present or a==nullptr, does nothing. + * Returns true if a had been present (and is now removed), else false. + */ + bool remove(sp<const AA> a); /** Removes the top element, if there is one. */ void pop(); /** Removes all elements. */ @@ -97,17 +100,17 @@ void indexed_priority_queue<AA, Comparator>::push(sp<const AA> a) { } template <class AA, class Comparator> -void indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) { - if (a == nullptr) return; - if (!contains(a)) return; +bool indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) { + if (a == nullptr) return false; + if (!contains(a)) return false; size_t idx = indices[a]; if (idx >= pq.size()) { - return; + return false; } if (idx == size()) { // if a is the last element, i.e. at index idx == size() == (pq.size()-1) pq.pop_back(); indices.erase(a); - return; + return true; } // move last element (guaranteed not to be at idx) to idx, then delete a sp<const AA> last_a = pq.back(); @@ -119,6 +122,8 @@ void indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) { // get the heap back in order (since the element at idx is not in order) sift_up(idx); sift_down(idx); + + return true; } // The same as, but slightly more efficient than, remove(top()). diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index d8099b34f39a..50cd130e9f39 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "SimpleConditionTracker.h" +#include "guardrail/StatsdStats.h" #include <log/logprint.h> @@ -32,9 +33,10 @@ using std::unordered_map; using std::vector; SimpleConditionTracker::SimpleConditionTracker( - const string& name, const int index, const SimpleCondition& simpleCondition, + const ConfigKey& key, const string& name, const int index, + const SimpleCondition& simpleCondition, const unordered_map<string, int>& trackerNameIndexMap) - : ConditionTracker(name, index) { + : ConditionTracker(name, index), mConfigKey(key) { VLOG("creating SimpleConditionTracker %s", mName.c_str()); mCountNesting = simpleCondition.count_nesting(); @@ -126,6 +128,24 @@ void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditio conditionCache[mIndex] = ConditionState::kFalse; } +bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) { + if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) { + // if the condition is not sliced or the key is not new, we are good! + return false; + } + // 1. Report the tuple count if the tuple count > soft limit + if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mSlicedConditionState.size() + 1; + StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("Condition %s dropping data for dimension key %s", mName.c_str(), newKey.c_str()); + return true; + } + } + return false; +} + void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart, std::vector<ConditionState>& conditionCache, @@ -133,6 +153,12 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou bool changed = false; auto outputIt = mSlicedConditionState.find(outputKey); ConditionState newCondition; + if (hitGuardRail(outputKey)) { + conditionChangedCache[mIndex] = false; + // Tells the caller it's evaluated. + conditionCache[mIndex] = ConditionState::kUnknown; + return; + } if (outputIt == mSlicedConditionState.end()) { // We get a new output key. newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index c287d8f34e8f..d21afd1d8083 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -19,6 +19,7 @@ #include <gtest/gtest_prod.h> #include "ConditionTracker.h" +#include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" @@ -28,7 +29,7 @@ namespace statsd { class SimpleConditionTracker : public virtual ConditionTracker { public: - SimpleConditionTracker(const std::string& name, const int index, + SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index, const SimpleCondition& simpleCondition, const std::unordered_map<std::string, int>& trackerNameIndexMap); @@ -50,6 +51,7 @@ public: std::vector<ConditionState>& conditionCache) const override; private: + const ConfigKey mConfigKey; // The index of the LogEventMatcher which defines the start. int mStartLogMatcherIndex; @@ -75,6 +77,8 @@ private: std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache); + bool hitGuardRail(const HashableDimensionKey& newKey); + FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition); FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim); FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll); diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index ac2192cdb3c5..445ee59947e9 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -210,6 +210,9 @@ StatsdConfig build_fake_config() { alert->set_number_of_buckets(6); alert->set_trigger_if_sum_gt(10); alert->set_refractory_period_secs(30); + Alert::IncidentdDetails* details = alert->mutable_incidentd_details(); + details->add_section(12); + details->add_section(13); // Count process state changes, slice by uid. metric = config.add_count_metric(); @@ -226,6 +229,9 @@ StatsdConfig build_fake_config() { alert->set_number_of_buckets(4); alert->set_trigger_if_sum_gt(30); alert->set_refractory_period_secs(20); + details = alert->mutable_incidentd_details(); + details->add_section(14); + details->add_section(15); // Count process state changes, slice by uid, while SCREEN_IS_OFF metric = config.add_count_metric(); diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp new file mode 100644 index 000000000000..2ab1146208ee --- /dev/null +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -0,0 +1,358 @@ +/* + * Copyright 2017, 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. + */ +#define DEBUG true // STOPSHIP if true +#include "Log.h" + +#include "StatsdStats.h" + +#include <android/util/ProtoOutputStream.h> +#include "statslog.h" + +namespace android { +namespace os { +namespace statsd { + +using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_BOOL; +using android::util::FIELD_TYPE_FLOAT; +using android::util::FIELD_TYPE_INT32; +using android::util::FIELD_TYPE_INT64; +using android::util::FIELD_TYPE_MESSAGE; +using android::util::FIELD_TYPE_STRING; +using android::util::ProtoOutputStream; +using std::lock_guard; +using std::map; +using std::string; +using std::vector; + +const int FIELD_ID_BEGIN_TIME = 1; +const int FIELD_ID_END_TIME = 2; +const int FIELD_ID_CONFIG_STATS = 3; +const int FIELD_ID_MATCHER_STATS = 4; +const int FIELD_ID_CONDITION_STATS = 5; +const int FIELD_ID_METRIC_STATS = 6; +const int FIELD_ID_ATOM_STATS = 7; + +const int FIELD_ID_MATCHER_STATS_NAME = 1; +const int FIELD_ID_MATCHER_STATS_COUNT = 2; + +const int FIELD_ID_CONDITION_STATS_NAME = 1; +const int FIELD_ID_CONDITION_STATS_COUNT = 2; + +const int FIELD_ID_METRIC_STATS_NAME = 1; +const int FIELD_ID_METRIC_STATS_COUNT = 2; + +const int FIELD_ID_ATOM_STATS_TAG = 1; +const int FIELD_ID_ATOM_STATS_COUNT = 2; + +// TODO: add stats for pulled atoms. +StatsdStats::StatsdStats() { + mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1); + mStartTimeSec = time(nullptr); +} + +StatsdStats& StatsdStats::getInstance() { + static StatsdStats statsInstance; + return statsInstance; +} + +void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount, + int matchersCount, int alertsCount, bool isValid) { + lock_guard<std::mutex> lock(mLock); + int32_t nowTimeSec = time(nullptr); + + // If there is an existing config for the same key, icebox the old config. + noteConfigRemovedInternalLocked(key); + + StatsdStatsReport_ConfigStats configStats; + configStats.set_uid(key.GetUid()); + configStats.set_name(key.GetName()); + configStats.set_creation_time_sec(nowTimeSec); + configStats.set_metric_count(metricsCount); + configStats.set_condition_count(conditionsCount); + configStats.set_matcher_count(matchersCount); + configStats.set_alert_count(alertsCount); + configStats.set_is_valid(isValid); + + if (isValid) { + mConfigStats[key] = configStats; + } else { + configStats.set_deletion_time_sec(nowTimeSec); + mIceBox.push_back(configStats); + } +} + +void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) { + auto it = mConfigStats.find(key); + if (it != mConfigStats.end()) { + int32_t nowTimeSec = time(nullptr); + it->second.set_deletion_time_sec(nowTimeSec); + // Add condition stats, metrics stats, matcher stats + addSubStatsToConfig(key, it->second); + // Remove them after they are added to the config stats. + mMatcherStats.erase(key); + mMetricsStats.erase(key); + mConditionStats.erase(key); + mIceBox.push_back(it->second); + mConfigStats.erase(it); + } +} + +void StatsdStats::noteConfigRemoved(const ConfigKey& key) { + lock_guard<std::mutex> lock(mLock); + noteConfigRemovedInternalLocked(key); +} + +void StatsdStats::noteBroadcastSent(const ConfigKey& key) { + lock_guard<std::mutex> lock(mLock); + auto it = mConfigStats.find(key); + if (it == mConfigStats.end()) { + ALOGE("Config key %s not found!", key.ToString().c_str()); + return; + } + + it->second.add_broadcast_sent_time_sec(time(nullptr)); +} + +void StatsdStats::noteDataDropped(const ConfigKey& key) { + lock_guard<std::mutex> lock(mLock); + auto it = mConfigStats.find(key); + if (it == mConfigStats.end()) { + ALOGE("Config key %s not found!", key.ToString().c_str()); + return; + } + + it->second.add_data_drop_time_sec(time(nullptr)); +} + +void StatsdStats::noteMetricsReportSent(const ConfigKey& key) { + lock_guard<std::mutex> lock(mLock); + auto it = mConfigStats.find(key); + if (it == mConfigStats.end()) { + ALOGE("Config key %s not found!", key.ToString().c_str()); + return; + } + + it->second.add_dump_report_time_sec(time(nullptr)); +} + +void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) { + lock_guard<std::mutex> lock(mLock); + // if name doesn't exist before, it will create the key with count 0. + auto& conditionSizeMap = mConditionStats[key]; + if (size > conditionSizeMap[name]) { + conditionSizeMap[name] = size; + } +} + +void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const string& name, int size) { + lock_guard<std::mutex> lock(mLock); + // if name doesn't exist before, it will create the key with count 0. + auto& metricsDimensionMap = mMetricsStats[key]; + if (size > metricsDimensionMap[name]) { + metricsDimensionMap[name] = size; + } +} + +void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) { + lock_guard<std::mutex> lock(mLock); + auto& matcherStats = mMatcherStats[key]; + matcherStats[name]++; +} + +void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) { + lock_guard<std::mutex> lock(mLock); + + if (timeSec < mStartTimeSec) { + return; + } + + if (atomId > android::util::kMaxPushedAtomId) { + ALOGW("not interested in atom %d", atomId); + return; + } + + mPushedAtomStats[atomId]++; +} + +void StatsdStats::reset() { + lock_guard<std::mutex> lock(mLock); + resetInternalLocked(); +} + +void StatsdStats::resetInternalLocked() { + // Reset the historical data, but keep the active ConfigStats + mStartTimeSec = time(nullptr); + mIceBox.clear(); + mConditionStats.clear(); + mMetricsStats.clear(); + std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0); + mMatcherStats.clear(); +} + +void StatsdStats::addSubStatsToConfig(const ConfigKey& key, + StatsdStatsReport_ConfigStats& configStats) { + // Add matcher stats + if (mMatcherStats.find(key) != mMatcherStats.end()) { + const auto& matcherStats = mMatcherStats[key]; + for (const auto& stats : matcherStats) { + auto output = configStats.add_matcher_stats(); + output->set_name(stats.first); + output->set_matched_times(stats.second); + VLOG("matcher %s matched %d times", stats.first.c_str(), stats.second); + } + } + // Add condition stats + if (mConditionStats.find(key) != mConditionStats.end()) { + const auto& conditionStats = mConditionStats[key]; + for (const auto& stats : conditionStats) { + auto output = configStats.add_condition_stats(); + output->set_name(stats.first); + output->set_max_tuple_counts(stats.second); + VLOG("condition %s max output tuple size %d", stats.first.c_str(), stats.second); + } + } + // Add metrics stats + if (mMetricsStats.find(key) != mMetricsStats.end()) { + const auto& conditionStats = mMetricsStats[key]; + for (const auto& stats : conditionStats) { + auto output = configStats.add_metric_stats(); + output->set_name(stats.first); + output->set_max_tuple_counts(stats.second); + VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second); + } + } +} + +void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) { + lock_guard<std::mutex> lock(mLock); + + if (DEBUG) { + time_t t = mStartTimeSec; + struct tm* tm = localtime(&t); + char timeBuffer[80]; + strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm); + VLOG("=================StatsdStats dump begins===================="); + VLOG("Stats collection start second: %s", timeBuffer); + } + ProtoOutputStream proto; + proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTimeSec); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr)); + + VLOG("%lu Config in icebox: ", (unsigned long)mIceBox.size()); + for (const auto& configStats : mIceBox) { + const int numBytes = configStats.ByteSize(); + vector<char> buffer(numBytes); + configStats.SerializeToArray(&buffer[0], numBytes); + proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0], + buffer.size()); + + // surround the whole block with DEBUG, so that compiler can strip out the code + // in production. + if (DEBUG) { + VLOG("*****ICEBOX*****"); + VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " + "#matcher=%d, #alert=%d, #valid=%d", + configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(), + configStats.deletion_time_sec(), configStats.metric_count(), + configStats.condition_count(), configStats.matcher_count(), + configStats.alert_count(), configStats.is_valid()); + + for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { + VLOG("\tbroadcast time: %d", broadcastTime); + } + + for (const auto& dataDropTime : configStats.data_drop_time_sec()) { + VLOG("\tdata drop time: %d", dataDropTime); + } + } + } + + for (auto& pair : mConfigStats) { + auto& configStats = pair.second; + if (DEBUG) { + VLOG("********Active Configs***********"); + VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, " + "#matcher=%d, #alert=%d, #valid=%d", + configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(), + configStats.deletion_time_sec(), configStats.metric_count(), + configStats.condition_count(), configStats.matcher_count(), + configStats.alert_count(), configStats.is_valid()); + for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) { + VLOG("\tbroadcast time: %d", broadcastTime); + } + + for (const auto& dataDropTime : configStats.data_drop_time_sec()) { + VLOG("\tdata drop time: %d", dataDropTime); + } + + for (const auto& dumpTime : configStats.dump_report_time_sec()) { + VLOG("\tdump report time: %d", dumpTime); + } + } + + addSubStatsToConfig(pair.first, configStats); + + const int numBytes = configStats.ByteSize(); + vector<char> buffer(numBytes); + configStats.SerializeToArray(&buffer[0], numBytes); + proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0], + buffer.size()); + // reset the sub stats, the source of truth is in the individual map + // they will be repopulated when dumpStats() is called again. + configStats.clear_matcher_stats(); + configStats.clear_condition_stats(); + configStats.clear_metric_stats(); + } + + VLOG("********Atom stats***********"); + const size_t atomCounts = mPushedAtomStats.size(); + for (size_t i = 2; i < atomCounts; i++) { + if (mPushedAtomStats[i] > 0) { + long long token = + proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i); + proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]); + proto.end(token); + + VLOG("Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]); + } + } + + output->clear(); + size_t bufferSize = proto.size(); + output->resize(bufferSize); + + size_t pos = 0; + auto it = proto.data(); + while (it.readBuffer() != NULL) { + size_t toRead = it.currentToRead(); + std::memcpy(&((*output)[pos]), it.readBuffer(), toRead); + pos += toRead; + it.rp()->move(toRead); + } + + if (reset) { + resetInternalLocked(); + } + + VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize); + VLOG("=================StatsdStats dump ends===================="); +} + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h new file mode 100644 index 000000000000..6fd9e4b6cd33 --- /dev/null +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -0,0 +1,174 @@ +/* + * Copyright 2017, 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. + */ +#pragma once + +#include "config/ConfigKey.h" +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" + +#include <gtest/gtest_prod.h> +#include <mutex> +#include <string> +#include <vector> + +namespace android { +namespace os { +namespace statsd { + +// Keeps track of stats of statsd. +// Single instance shared across the process. All methods are thread safe. +class StatsdStats { +public: + static StatsdStats& getInstance(); + ~StatsdStats(){}; + + // TODO: set different limit if the device is low ram. + const static int kDimensionKeySizeSoftLimit = 300; + const static int kDimensionKeySizeHardLimit = 500; + + const static int kMaxConfigCount = 10; + const static int kMaxConditionCountPerConfig = 200; + const static int kMaxMetricCountPerConfig = 300; + const static int kMaxMatcherCountPerConfig = 500; + + /** + * Report a new config has been received and report the static stats about the config. + * + * The static stats include: the count of metrics, conditions, matchers, and alerts. + * If the config is not valid, this config stats will be put into icebox immediately. + */ + void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount, + int matchersCount, int alertCount, bool isValid); + /** + * Report a config has been removed. + */ + void noteConfigRemoved(const ConfigKey& key); + + /** + * Report a broadcast has been sent to a config owner to collect the data. + */ + void noteBroadcastSent(const ConfigKey& key); + + /** + * Report a config's metrics data has been dropped. + */ + void noteDataDropped(const ConfigKey& key); + + /** + * Report metrics data report has been sent. + * + * The report may be requested via StatsManager API, or through adb cmd. + */ + void noteMetricsReportSent(const ConfigKey& key); + + /** + * Report the size of output tuple of a condition. + * + * Note: only report when the condition has an output dimension, and the tuple + * count > kDimensionKeySizeSoftLimit. + * + * [key]: The config key that this condition belongs to. + * [name]: The name of the condition. + * [size]: The output tuple size. + */ + void noteConditionDimensionSize(const ConfigKey& key, const std::string& name, int size); + + /** + * Report the size of output tuple of a metric. + * + * Note: only report when the metric has an output dimension, and the tuple + * count > kDimensionKeySizeSoftLimit. + * + * [key]: The config key that this metric belongs to. + * [name]: The name of the metric. + * [size]: The output tuple size. + */ + void noteMetricDimensionSize(const ConfigKey& key, const std::string& name, int size); + + /** + * Report a matcher has been matched. + * + * [key]: The config key that this matcher belongs to. + * [name]: The name of the matcher. + */ + void noteMatcherMatched(const ConfigKey& key, const std::string& name); + + /** + * Report an atom event has been logged. + */ + void noteAtomLogged(int atomId, int32_t timeSec); + + /** + * Reset the historical stats. Including all stats in icebox, and the tracked stats about + * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue + * to collect stats after reset() has been called. + */ + void reset(); + + /** + * Output the stats in protobuf binary format to [buffer]. + * + * [reset]: whether to clear the historical stats after the call. + */ + void dumpStats(std::vector<uint8_t>* buffer, bool reset); + +private: + StatsdStats(); + + mutable std::mutex mLock; + + int32_t mStartTimeSec; + + // The stats about the configs that are still in use. + std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats; + + // Stores the stats for the configs that are no longer in use. + std::vector<const StatsdStatsReport_ConfigStats> mIceBox; + + // Stores the number of output tuple of condition trackers when it's bigger than + // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, + // it means some data has been dropped. + std::map<const ConfigKey, std::map<const std::string, int>> mConditionStats; + + // Stores the number of output tuple of metric producers when it's bigger than + // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1, + // it means some data has been dropped. + std::map<const ConfigKey, std::map<const std::string, int>> mMetricsStats; + + // Stores the number of times a pushed atom is logged. + // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms + // out of that range will be dropped (it's either pulled atoms or test atoms). + // This is a vector, not a map because it will be accessed A LOT -- for each stats log. + std::vector<int> mPushedAtomStats; + + // Stores how many times a matcher have been matched. + std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats; + + void noteConfigRemovedInternalLocked(const ConfigKey& key); + + void resetInternalLocked(); + + void addSubStatsToConfig(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats); + + FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd); + FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd); + FRIEND_TEST(StatsdStatsTest, TestConfigRemove); + FRIEND_TEST(StatsdStatsTest, TestSubStats); + FRIEND_TEST(StatsdStatsTest, TestAtomLog); +}; + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h index ffbf2488ad2c..fea3e9b367ef 100644 --- a/cmds/statsd/src/matchers/LogMatchingTracker.h +++ b/cmds/statsd/src/matchers/LogMatchingTracker.h @@ -69,6 +69,10 @@ public: return mTagIds; } + const std::string& getName() const { + return mName; + } + protected: // Name of this matching. We don't really need the name, but it makes log message easy to debug. const std::string mName; diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp index b2c88a0d3013..ad37b010d1fb 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp @@ -19,8 +19,6 @@ #include "SimpleLogMatchingTracker.h" -#include <log/logprint.h> - namespace android { namespace os { namespace statsd { diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 149b9c16fd88..4338399bf675 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "CountMetricProducer.h" +#include "guardrail/StatsdStats.h" #include "stats_util.h" #include <limits.h> @@ -61,12 +62,11 @@ const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; const int FIELD_ID_COUNT = 3; -// TODO: add back AnomalyTracker. - -CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex, +CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric, + const int conditionIndex, const sp<ConditionWizard>& wizard, const uint64_t startTimeNs) - : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) { + : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; @@ -94,6 +94,8 @@ CountMetricProducer::~CountMetricProducer() { } void CountMetricProducer::startNewProtoOutputStream(long long startTime) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + mProto = std::make_unique<ProtoOutputStream>(); mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); @@ -107,13 +109,8 @@ void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); } -std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() { - long long endTime = time(nullptr) * NS_PER_SEC; - - // Dump current bucket if it's stale. - // If current bucket is still on-going, don't force dump current bucket. - // In finish(), We can force dump current bucket. - flushIfNeeded(endTime); +void CountMetricProducer::serializeBuckets() { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); VLOG("metric %s dump report now...", mMetric.name().c_str()); for (const auto& counter : mPastBuckets) { @@ -159,27 +156,59 @@ std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() { } mProto->end(wrapperToken); } - mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); + mPastBuckets.clear(); + // TODO: Clear mDimensionKeyMap once the report is dumped. +} + +std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() { + long long endTime = time(nullptr) * NS_PER_SEC; VLOG("metric %s dump report now...", mMetric.name().c_str()); + // Dump current bucket if it's stale. + // If current bucket is still on-going, don't force dump current bucket. + // In finish(), We can force dump current bucket. + flushIfNeeded(endTime); + + // TODO(yanglu): merge these three functions to one to avoid three locks. + serializeBuckets(); + std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(endTime); - mPastBuckets.clear(); return buffer; - - // TODO: Clear mDimensionKeyMap once the report is dumped. } void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mCondition = conditionMet; } +bool CountMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); + if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) { + return false; + } + // ===========GuardRail============== + // 1. Report the tuple count if the tuple count > soft limit + if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mCurrentSlicedCounter->size() + 1; + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(), + newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("CountMetric %s dropping data for dimension key %s", mMetric.name().c_str(), + newKey.c_str()); + return true; + } + } + + return false; +} void CountMetricProducer::onMatchedLogEventInternal( const size_t matcherIndex, const HashableDimensionKey& eventKey, const map<string, HashableDimensionKey>& conditionKey, bool condition, @@ -188,33 +217,40 @@ void CountMetricProducer::onMatchedLogEventInternal( flushIfNeeded(eventTimeNs); - if (condition == false) { + // ===========GuardRail============== + if (hitGuardRail(eventKey)) { return; } - auto it = mCurrentSlicedCounter->find(eventKey); - - if (it == mCurrentSlicedCounter->end()) { - // create a counter for the new key - (*mCurrentSlicedCounter)[eventKey] = 1; - } else { - // increment the existing value - auto& count = it->second; - count++; - } + // TODO(yanglu): move the following logic to a seperate function to make it lockable. + { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + if (condition == false) { + return; + } - for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, - mCurrentSlicedCounter->find(eventKey)->second); + auto it = mCurrentSlicedCounter->find(eventKey); + if (it == mCurrentSlicedCounter->end()) { + // create a counter for the new key + mCurrentSlicedCounter->insert({eventKey, 1}); + } else { + // increment the existing value + auto& count = it->second; + count++; + } + const int64_t& count = mCurrentSlicedCounter->find(eventKey)->second; + for (auto& tracker : mAnomalyTrackers) { + tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, count); + } + VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(), (long long)(count)); } - - VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(), - (long long)(*mCurrentSlicedCounter)[eventKey]); } // When a new matched event comes in, we check if event falls into the current // bucket. If not, flush the old counter to past buckets and initialize the new bucket. void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) { return; } @@ -248,6 +284,7 @@ void CountMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { // greater than actual data size as it contains each dimension of // CountMetricData is duplicated. size_t CountMetricProducer::byteSize() const { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); size_t totalSize = 0; for (const auto& pair : mPastBuckets) { totalSize += pair.second.size() * kBucketSize; diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 293e5b971fa5..164dfb25c01e 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -42,8 +42,9 @@ struct CountBucket { class CountMetricProducer : public MetricProducer { public: // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics. - CountMetricProducer(const CountMetric& countMetric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const uint64_t startTimeNs); + CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric, + const int conditionIndex, const sp<ConditionWizard>& wizard, + const uint64_t startTimeNs); virtual ~CountMetricProducer(); @@ -74,6 +75,8 @@ protected: void startNewProtoOutputStream(long long timestamp) override; private: + void serializeBuckets(); + const CountMetric mMetric; // TODO: Add a lock to mPastBuckets. @@ -84,6 +87,8 @@ private: static const size_t kBucketSize = sizeof(CountBucket{}); + bool hitGuardRail(const HashableDimensionKey& newKey); + FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents); FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 0ee7dcdf3c2e..3b49b9aadcde 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "DurationMetricProducer.h" +#include "guardrail/StatsdStats.h" #include "stats_util.h" #include <limits.h> @@ -60,14 +61,14 @@ const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; const int FIELD_ID_DURATION = 3; -DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric, +DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs) - : MetricProducer(startTimeNs, conditionIndex, wizard), + : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric), mStartIndex(startIndex), mStopIndex(stopIndex), @@ -103,6 +104,7 @@ DurationMetricProducer::~DurationMetricProducer() { } void DurationMetricProducer::startNewProtoOutputStream(long long startTime) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mProto = std::make_unique<ProtoOutputStream>(); mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); @@ -110,16 +112,16 @@ void DurationMetricProducer::startNewProtoOutputStream(long long startTime) { } unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( - const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) { + const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) const { switch (mMetric.aggregation_type()) { case DurationMetric_AggregationType_SUM: - return make_unique<OringDurationTracker>(eventKey, mWizard, mConditionTrackerIndex, - mNested, mCurrentBucketStartTimeNs, - mBucketSizeNs, mAnomalyTrackers, bucket); + return make_unique<OringDurationTracker>( + mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested, + mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket); case DurationMetric_AggregationType_MAX_SPARSE: - return make_unique<MaxDurationTracker>(eventKey, mWizard, mConditionTrackerIndex, - mNested, mCurrentBucketStartTimeNs, - mBucketSizeNs, mAnomalyTrackers, bucket); + return make_unique<MaxDurationTracker>( + mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested, + mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket); } } @@ -129,7 +131,9 @@ void DurationMetricProducer::finish() { } void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); + flushIfNeeded(eventTime); // Now for each of the on-going event, check if the condition has changed for them. for (auto& pair : mCurrentSlicedDuration) { pair.second->onSlicedConditionMayChange(eventTime); @@ -137,8 +141,10 @@ void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime } void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); mCondition = conditionMet; + flushIfNeeded(eventTime); // TODO: need to populate the condition change time from the event which triggers the condition // change, instead of using current time. for (auto& pair : mCurrentSlicedDuration) { @@ -146,15 +152,8 @@ void DurationMetricProducer::onConditionChanged(const bool conditionMet, const u } } -std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() { - long long endTime = time(nullptr) * NS_PER_SEC; - - // Dump current bucket if it's stale. - // If current bucket is still on-going, don't force dump current bucket. - // In finish(), We can force dump current bucket. - flushIfNeeded(endTime); - VLOG("metric %s dump report now...", mMetric.name().c_str()); - +void DurationMetricProducer::SerializeBuckets() { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; VLOG(" dimension key %s", hashableKey.c_str()); @@ -211,13 +210,29 @@ std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() { mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); +} + +std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() { + VLOG("metric %s dump report now...", mMetric.name().c_str()); + + long long endTime = time(nullptr) * NS_PER_SEC; + + // Dump current bucket if it's stale. + // If current bucket is still on-going, don't force dump current bucket. + // In finish(), We can force dump current bucket. + flushIfNeeded(endTime); + + SerializeBuckets(); + std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); + startNewProtoOutputStream(endTime); // TODO: Properly clear the old buckets. return buffer; } void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { return; } @@ -236,35 +251,64 @@ void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) { mCurrentBucketNum += numBucketsForward; } +bool DurationMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); + // the key is not new, we are good. + if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) { + return false; + } + // 1. Report the tuple count if the tuple count > soft limit + if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mCurrentSlicedDuration.size() + 1; + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(), + newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("DurationMetric %s dropping data for dimension key %s", mMetric.name().c_str(), + newKey.c_str()); + return true; + } + } + return false; +} + void DurationMetricProducer::onMatchedLogEventInternal( const size_t matcherIndex, const HashableDimensionKey& eventKey, const map<string, HashableDimensionKey>& conditionKeys, bool condition, const LogEvent& event, bool scheduledPull) { flushIfNeeded(event.GetTimestampNs()); - if (matcherIndex == mStopAllIndex) { - for (auto& pair : mCurrentSlicedDuration) { - pair.second->noteStopAll(event.GetTimestampNs()); + // TODO(yanglu): move the following logic to a seperate function to make it lockable. + { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + if (matcherIndex == mStopAllIndex) { + for (auto& pair : mCurrentSlicedDuration) { + pair.second->noteStopAll(event.GetTimestampNs()); + } + return; } - return; - } - HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension)); + HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension)); - if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) { - mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]); - } - - auto it = mCurrentSlicedDuration.find(eventKey); + if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) { + if (hitGuardRail(eventKey)) { + return; + } + mCurrentSlicedDuration[eventKey] = + createDurationTracker(eventKey, mPastBuckets[eventKey]); + } + auto it = mCurrentSlicedDuration.find(eventKey); - if (matcherIndex == mStartIndex) { - it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys); - } else if (matcherIndex == mStopIndex) { - it->second->noteStop(atomKey, event.GetTimestampNs(), false); + if (matcherIndex == mStartIndex) { + it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys); + } else if (matcherIndex == mStopIndex) { + it->second->noteStop(atomKey, event.GetTimestampNs(), false); + } } } size_t DurationMetricProducer::byteSize() const { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); size_t totalSize = 0; for (const auto& pair : mPastBuckets) { totalSize += pair.second.size() * kBucketSize; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index ebd5e8d36412..68fff484b64d 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -37,9 +37,9 @@ namespace statsd { class DurationMetricProducer : public MetricProducer { public: - DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex, - const size_t startIndex, const size_t stopIndex, - const size_t stopAllIndex, const bool nesting, + DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric, + const int conditionIndex, const size_t startIndex, + const size_t stopIndex, const size_t stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs); @@ -71,6 +71,8 @@ protected: void startNewProtoOutputStream(long long timestamp) override; private: + void SerializeBuckets(); + const DurationMetric mMetric; // Index of the SimpleLogEntryMatcher which defines the start. @@ -96,8 +98,9 @@ private: std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>> mCurrentSlicedDuration; - std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey, - std::vector<DurationBucket>& bucket); + std::unique_ptr<DurationTracker> createDurationTracker( + const HashableDimensionKey& eventKey, std::vector<DurationBucket>& bucket) const; + bool hitGuardRail(const HashableDimensionKey& newKey); static const size_t kBucketSize = sizeof(DurationBucket{}); }; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 567b4c77bf7b..53f112a507ce 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -51,10 +51,11 @@ const int FIELD_ID_DATA = 1; const int FIELD_ID_TIMESTAMP_NANOS = 1; const int FIELD_ID_ATOMS = 2; -EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex, +EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric, + const int conditionIndex, const sp<ConditionWizard>& wizard, const uint64_t startTimeNs) - : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) { + : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) { if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), metric.links().end()); @@ -72,6 +73,7 @@ EventMetricProducer::~EventMetricProducer() { } void EventMetricProducer::startNewProtoOutputStream(long long startTime) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mProto = std::make_unique<ProtoOutputStream>(); // TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData, // and StatsEvent. @@ -88,11 +90,16 @@ void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() { long long endTime = time(nullptr) * NS_PER_SEC; - mProto->end(mProtoToken); - mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime); + // TODO(yanglu): make this section to an util function. + { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + mProto->end(mProtoToken); + mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime); + + size_t bufferSize = mProto->size(); + VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize); + } - size_t bufferSize = mProto->size(); - VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(endTime); @@ -102,6 +109,7 @@ std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() { void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mCondition = conditionMet; } @@ -109,6 +117,7 @@ void EventMetricProducer::onMatchedLogEventInternal( const size_t matcherIndex, const HashableDimensionKey& eventKey, const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, const LogEvent& event, bool scheduledPull) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (!condition) { return; } @@ -123,6 +132,7 @@ void EventMetricProducer::onMatchedLogEventInternal( } size_t EventMetricProducer::byteSize() const { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); return mProto->bytesWritten(); } diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 5afcebd29e58..33a951035dca 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -34,8 +34,9 @@ namespace statsd { class EventMetricProducer : public MetricProducer { public: // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics. - EventMetricProducer(const EventMetric& eventMetric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const uint64_t startTimeNs); + EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric, + const int conditionIndex, const sp<ConditionWizard>& wizard, + const uint64_t startTimeNs); virtual ~EventMetricProducer(); diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index be030d856aee..ed4c76083303 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "GaugeMetricProducer.h" +#include "guardrail/StatsdStats.h" #include "stats_util.h" #include <cutils/log.h> @@ -62,10 +63,11 @@ const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; const int FIELD_ID_GAUGE = 3; -GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex, +GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric, + const int conditionIndex, const sp<ConditionWizard>& wizard, const int pullTagId, const int64_t startTimeNs) - : MetricProducer(startTimeNs, conditionIndex, wizard), + : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric), mPullTagId(pullTagId) { if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { @@ -100,6 +102,7 @@ GaugeMetricProducer::~GaugeMetricProducer() { } void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mProto = std::make_unique<ProtoOutputStream>(); mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); @@ -109,14 +112,8 @@ void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) { void GaugeMetricProducer::finish() { } -std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() { - VLOG("gauge metric %s dump report now...", mMetric.name().c_str()); - - // Dump current bucket if it's stale. - // If current bucket is still on-going, don't force dump current bucket. - // In finish(), We can force dump current bucket. - flushIfNeeded(time(nullptr) * NS_PER_SEC); - +void GaugeMetricProducer::SerializeBuckets() { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; auto it = mDimensionKeyMap.find(hashableKey); @@ -164,50 +161,69 @@ std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() { mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); + mPastBuckets.clear(); +} + +std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() { + VLOG("gauge metric %s dump report now...", mMetric.name().c_str()); + + // Dump current bucket if it's stale. + // If current bucket is still on-going, don't force dump current bucket. + // In finish(), We can force dump current bucket. + flushIfNeeded(time(nullptr) * NS_PER_SEC); + + SerializeBuckets(); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(time(nullptr) * NS_PER_SEC); - mPastBuckets.clear(); - return buffer; // TODO: Clear mDimensionKeyMap once the report is dumped. } void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) { - AutoMutex _l(mLock); VLOG("Metric %s onConditionChanged", mMetric.name().c_str()); + + // flushIfNeeded holds the write lock and is thread-safe. flushIfNeeded(eventTime); - mCondition = conditionMet; - // Push mode. No need to proactively pull the gauge data. - if (mPullTagId == -1) { - return; - } - if (!mCondition) { - return; - } - // Already have gauge metric for the current bucket, do not do it again. - if (mCurrentSlicedBucket->size() > 0) { - return; - } vector<std::shared_ptr<LogEvent>> allData; - if (!mStatsPullerManager.Pull(mPullTagId, &allData)) { - ALOGE("Stats puller failed for tag: %d", mPullTagId); - return; + // The following section is to update the condition and re-pull the gauge. + // TODO(yanglu): make it a seperate lockable function. + { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + + mCondition = conditionMet; + + // Push mode. No need to proactively pull the gauge data. + if (mPullTagId == -1) { + return; + } + if (!mCondition) { + return; + } + // Already have gauge metric for the current bucket, do not do it again. + if (mCurrentSlicedBucket->size() > 0) { + return; + } + if (!mStatsPullerManager.Pull(mPullTagId, &allData)) { + ALOGE("Stats puller failed for tag: %d", mPullTagId); + return; + } } + + // onMatchedLogEventInternal holds the write lock and is thread-safe. for (const auto& data : allData) { onMatchedLogEvent(0, *data, false /*scheduledPull*/); } - flushIfNeeded(eventTime); } void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); } -int64_t GaugeMetricProducer::getGauge(const LogEvent& event) { +int64_t GaugeMetricProducer::getGauge(const LogEvent& event) const { status_t err = NO_ERROR; int64_t val = event.GetLong(mMetric.gauge_field(), &err); if (err == NO_ERROR) { @@ -219,39 +235,63 @@ int64_t GaugeMetricProducer::getGauge(const LogEvent& event) { } void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { - AutoMutex mutex(mLock); + // onMatchedLogEventInternal holds the write lock and is thread-safe. for (const auto& data : allData) { onMatchedLogEvent(0, *data, true /*scheduledPull*/); } } +bool GaugeMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); + if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) { + return false; + } + // 1. Report the tuple count if the tuple count > soft limit + if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mCurrentSlicedBucket->size() + 1; + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(), + newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("GaugeMetric %s dropping data for dimension key %s", mMetric.name().c_str(), + newKey.c_str()); + return true; + } + } + + return false; +} + void GaugeMetricProducer::onMatchedLogEventInternal( const size_t matcherIndex, const HashableDimensionKey& eventKey, const map<string, HashableDimensionKey>& conditionKey, bool condition, const LogEvent& event, bool scheduledPull) { + uint64_t eventTimeNs = event.GetTimestampNs(); + flushIfNeeded(eventTimeNs); + if (condition == false) { return; } - uint64_t eventTimeNs = event.GetTimestampNs(); + const long gauge = getGauge(event); + if (gauge < 0) { + return; + } + if (hitGuardRail(eventKey)) { + return; + } + + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); return; } - // When the event happens in a new bucket, flush the old buckets. - if (eventTimeNs >= mCurrentBucketStartTimeNs + mBucketSizeNs) { - flushIfNeeded(eventTimeNs); - } - - // For gauge metric, we just simply use the first guage in the given bucket. + // For gauge metric, we just simply use the first gauge in the given bucket. if (!mCurrentSlicedBucket->empty()) { return; } - const long gauge = getGauge(event); - if (gauge >= 0) { (*mCurrentSlicedBucket)[eventKey] = gauge; - } for (auto& tracker : mAnomalyTrackers) { tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, gauge); } @@ -263,6 +303,7 @@ void GaugeMetricProducer::onMatchedLogEventInternal( // if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside // the GaugeMetricProducer while holding the lock. void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (eventTimeNs < mCurrentBucketStartTimeNs + mBucketSizeNs) { return; } @@ -296,6 +337,7 @@ void GaugeMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { } size_t GaugeMetricProducer::byteSize() const { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); size_t totalSize = 0; for (const auto& pair : mPastBuckets) { totalSize += pair.second.size() * kBucketSize; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 4b7654b4ca6b..8df61117d75d 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -47,9 +47,9 @@ class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDa public: // TODO: Pass in the start time from MetricsManager, it should be consistent // for all metrics. - GaugeMetricProducer(const GaugeMetric& countMetric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int pullTagId, - const int64_t startTimeNs); + GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& countMetric, + const int conditionIndex, const sp<ConditionWizard>& wizard, + const int pullTagId, const int64_t startTimeNs); virtual ~GaugeMetricProducer(); @@ -81,6 +81,8 @@ protected: void startNewProtoOutputStream(long long timestamp) override; private: + void SerializeBuckets(); + // The default bucket size for gauge metric is 1 second. static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000; const GaugeMetric mMetric; @@ -89,8 +91,6 @@ private: // tagId for pulled data. -1 if this is not pulled const int mPullTagId; - Mutex mLock; - // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets; @@ -98,7 +98,9 @@ private: // The current bucket. std::shared_ptr<DimToValMap> mCurrentSlicedBucket = std::make_shared<DimToValMap>(); - int64_t getGauge(const LogEvent& event); + int64_t getGauge(const LogEvent& event) const; + + bool hitGuardRail(const HashableDimensionKey& newKey); static const size_t kBucketSize = sizeof(GaugeBucket{}); diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 62fb632faaaf..7542a9439032 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -23,6 +23,7 @@ using std::map; void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event, bool scheduledPull) { + std::unique_lock<std::shared_timed_mutex> writeLock(mRWMutex); uint64_t eventTimeNs = event.GetTimestampNs(); // this is old event, maybe statsd restarted? if (eventTimeNs < mStartTimeNs) { @@ -59,12 +60,16 @@ void MetricProducer::onMatchedLogEvent(const size_t matcherIndex, const LogEvent } else { condition = mCondition; } + // Unlock as onMatchedLogEventInternal is threadsafe. + writeLock.unlock(); onMatchedLogEventInternal(matcherIndex, eventKey, conditionKeys, condition, event, scheduledPull); } std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProto() { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + size_t bufferSize = mProto->size(); std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize)); diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index ddccf9a1970b..27343ad6215a 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -17,8 +17,11 @@ #ifndef METRIC_PRODUCER_H #define METRIC_PRODUCER_H +#include <shared_mutex> + #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" +#include "config/ConfigKey.h" #include "matchers/matcher_util.h" #include "packages/PackageInfoListener.h" @@ -35,9 +38,10 @@ namespace statsd { // be a no-op. class MetricProducer : public virtual PackageInfoListener { public: - MetricProducer(const int64_t startTimeNs, const int conditionIndex, + MetricProducer(const ConfigKey& key, const int64_t startTimeNs, const int conditionIndex, const sp<ConditionWizard>& wizard) - : mStartTimeNs(startTimeNs), + : mConfigKey(key), + mStartTimeNs(startTimeNs), mCurrentBucketStartTimeNs(startTimeNs), mCurrentBucketNum(0), mCondition(conditionIndex >= 0 ? false : true), @@ -83,6 +87,8 @@ public: } protected: + const ConfigKey mConfigKey; + const uint64_t mStartTimeNs; uint64_t mCurrentBucketStartTimeNs; @@ -133,6 +139,10 @@ protected: long long mProtoToken; + // Read/Write mutex to make the producer thread-safe. + // TODO(yanglu): replace with std::shared_mutex when available in libc++. + mutable std::shared_timed_mutex mRWMutex; + virtual void startNewProtoOutputStream(long long timestamp) = 0; std::unique_ptr<std::vector<uint8_t>> serializeProto(); diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 5916b040889c..e986c1ab2ddb 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -20,6 +20,7 @@ #include "CountMetricProducer.h" #include "condition/CombinationConditionTracker.h" #include "condition/SimpleConditionTracker.h" +#include "guardrail/StatsdStats.h" #include "matchers/CombinationLogMatchingTracker.h" #include "matchers/SimpleLogMatchingTracker.h" #include "metrics_manager_util.h" @@ -36,10 +37,24 @@ namespace android { namespace os { namespace statsd { -MetricsManager::MetricsManager(const StatsdConfig& config) { - mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers, - mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, - mTrackerToMetricMap, mTrackerToConditionMap); +MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config) : mConfigKey(key) { + mConfigValid = + initStatsdConfig(key, config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers, + mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, + mTrackerToMetricMap, mTrackerToConditionMap); + + // TODO: add alert size. + // no matter whether this config is valid, log it in the stats. + StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(), + mAllConditionTrackers.size(), + mAllLogEntryMatchers.size(), 0, mConfigValid); + // Guardrail. Reject the config if it's too big. + if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig || + mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig || + mAllLogEntryMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) { + ALOGE("This config is too big! Reject!"); + mConfigValid = false; + } } MetricsManager::~MetricsManager() { @@ -137,6 +152,8 @@ void MetricsManager::onLogEvent(const LogEvent& event) { // For matched LogEntryMatchers, tell relevant metrics that a matched event has come. for (size_t i = 0; i < mAllLogEntryMatchers.size(); i++) { if (matcherCache[i] == MatchingState::kMatched) { + StatsdStats::getInstance().noteMatcherMatched(mConfigKey, + mAllLogEntryMatchers[i]->getName()); auto pair = mTrackerToMetricMap.find(i); if (pair != mTrackerToMetricMap.end()) { auto& metricList = pair->second; @@ -151,9 +168,9 @@ void MetricsManager::onLogEvent(const LogEvent& event) { } void MetricsManager::onAnomalyAlarmFired(const uint64_t timestampNs, - sp<const AnomalyAlarm> anomaly) { + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet) { for (const auto& itr : mAllAnomalyTrackers) { - itr->declareAnomaly(timestampNs); + itr->informAlarmsFired(timestampNs, anomalySet); } } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index a6054e353aa2..62b4c872b318 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -19,6 +19,7 @@ #include "anomaly/AnomalyMonitor.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionTracker.h" +#include "config/ConfigKey.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "logd/LogEvent.h" #include "matchers/LogMatchingTracker.h" @@ -33,7 +34,7 @@ namespace statsd { // A MetricsManager is responsible for managing metrics from one single config source. class MetricsManager { public: - MetricsManager(const StatsdConfig& config); + MetricsManager(const ConfigKey& configKey, const StatsdConfig& config); ~MetricsManager(); @@ -45,7 +46,8 @@ public: // Called when everything should wrap up. We are about to finish (e.g., new config comes). void finish(); - void onAnomalyAlarmFired(const uint64_t timestampNs, sp<const AnomalyAlarm> anomaly); + void onAnomalyAlarmFired(const uint64_t timestampNs, + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet); void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor); @@ -57,6 +59,8 @@ public: size_t byteSize(); private: + const ConfigKey mConfigKey; + // All event tags that are interesting to my metrics. std::set<int> mTagIds; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index cc02c69d4405..eed7841ab435 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -18,6 +18,7 @@ #include "Log.h" #include "ValueMetricProducer.h" +#include "guardrail/StatsdStats.h" #include <cutils/log.h> #include <limits.h> @@ -67,11 +68,12 @@ const int FIELD_ID_VALUE = 3; static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L; // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently -ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex, +ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric, + const int conditionIndex, const sp<ConditionWizard>& wizard, const int pullTagId, const uint64_t startTimeNs, shared_ptr<StatsPullerManager> statsPullerManager) - : MetricProducer(startTimeNs, conditionIndex, wizard), + : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric), mStatsPullerManager(statsPullerManager), mPullTagId(pullTagId) { @@ -103,10 +105,11 @@ ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int co } // for testing -ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex, +ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric, + const int conditionIndex, const sp<ConditionWizard>& wizard, const int pullTagId, const uint64_t startTimeNs) - : ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs, + : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs, make_shared<StatsPullerManager>()) { } @@ -118,6 +121,7 @@ ValueMetricProducer::~ValueMetricProducer() { } void ValueMetricProducer::startNewProtoOutputStream(long long startTime) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); mProto = std::make_unique<ProtoOutputStream>(); mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name()); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime); @@ -133,9 +137,8 @@ void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) { VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str()); } -std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() { - VLOG("metric %s dump report now...", mMetric.name().c_str()); - +void ValueMetricProducer::SerializeBuckets() { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; VLOG(" dimension key %s", hashableKey.c_str()); @@ -182,47 +185,63 @@ std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() { mProto->end(mProtoToken); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)mCurrentBucketStartTimeNs); + mPastBuckets.clear(); +} +std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() { VLOG("metric %s dump report now...", mMetric.name().c_str()); + + SerializeBuckets(); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(time(nullptr) * NS_PER_SEC); - mPastBuckets.clear(); return buffer; - // TODO: Clear mDimensionKeyMap once the report is dumped. } void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) { - AutoMutex _l(mLock); - mCondition = condition; + vector<shared_ptr<LogEvent>> allData; + + // TODO(yanglu): move the following logic to a seperate function to make it lockable. + { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + mCondition = condition; + + if (mPullTagId == -1) { + return; + } - if (mPullTagId != -1) { if (mCondition == true) { mStatsPullerManager->RegisterReceiver(mPullTagId, this, mMetric.bucket().bucket_size_millis()); } else if (mCondition == false) { mStatsPullerManager->UnRegisterReceiver(mPullTagId, this); } - - vector<shared_ptr<LogEvent>> allData; - if (mStatsPullerManager->Pull(mPullTagId, &allData)) { - if (allData.size() == 0) { - return; - } - for (const auto& data : allData) { - onMatchedLogEvent(0, *data, false); - } - flushIfNeeded(eventTime); + if (!mStatsPullerManager->Pull(mPullTagId, &allData)) { + return; } + } + + if (allData.size() == 0) { return; } + + // onMatchedLogEventInternal holds the write lock and is thread-safe. + for (const auto& data : allData) { + onMatchedLogEvent(0, *data, false); + } + // flushIfNeeded holds the write lock and is thread-safe. + flushIfNeeded(eventTime); +} + +bool ValueMetricProducer::IsConditionMet() const { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); + return mCondition == true || !mMetric.has_condition(); } void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { - AutoMutex _l(mLock); - if (mCondition == true || !mMetric.has_condition()) { + if (IsConditionMet()) { if (allData.size() == 0) { return; } @@ -238,55 +257,97 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven } } -void ValueMetricProducer::onMatchedLogEventInternal( - const size_t matcherIndex, const HashableDimensionKey& eventKey, - const map<string, HashableDimensionKey>& conditionKey, bool condition, - const LogEvent& event, bool scheduledPull) { - uint64_t eventTimeNs = event.GetTimestampNs(); +bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); + // ===========GuardRail============== + // 1. Report the tuple count if the tuple count > soft limit + if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) { + return false; + } + if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mCurrentSlicedBucket.size() + 1; + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(), + newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("ValueMetric %s dropping data for dimension key %s", mMetric.name().c_str(), + newKey.c_str()); + return true; + } + } + + return false; +} + +void ValueMetricProducer::onMatchedLogEventInternal_pull(const uint64_t& eventTimeNs, + const HashableDimensionKey& eventKey, + const long& value, bool scheduledPull) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); return; } - Interval& interval = mCurrentSlicedBucket[eventKey]; - - long value = get_value(event); - - if (mPullTagId != -1) { - if (scheduledPull) { - // scheduled pull always sets beginning of current bucket and end - // of next bucket - if (interval.raw.size() > 0) { - interval.raw.back().second = value; - } else { - interval.raw.push_back(make_pair(value, value)); - } - Interval& nextInterval = mNextSlicedBucket[eventKey]; - if (nextInterval.raw.size() == 0) { - nextInterval.raw.push_back(make_pair(value, 0)); - } else { - nextInterval.raw.front().first = value; - } + if (scheduledPull) { + // scheduled pull always sets beginning of current bucket and end + // of next bucket + if (interval.raw.size() > 0) { + interval.raw.back().second = value; + } else { + interval.raw.push_back(make_pair(value, value)); + } + Interval& nextInterval = mNextSlicedBucket[eventKey]; + if (nextInterval.raw.size() == 0) { + nextInterval.raw.push_back(make_pair(value, 0)); } else { - if (mCondition == true) { - interval.raw.push_back(make_pair(value, 0)); + nextInterval.raw.front().first = value; + } + } else { + if (mCondition == true) { + interval.raw.push_back(make_pair(value, 0)); + } else { + if (interval.raw.size() != 0) { + interval.raw.back().second = value; } else { - if (interval.raw.size() != 0) { - interval.raw.back().second = value; - } else { - interval.tainted = true; - VLOG("Data on condition true missing!"); - } + interval.tainted = true; + VLOG("Data on condition true missing!"); } } + } +} + +void ValueMetricProducer::onMatchedLogEventInternal_push(const uint64_t& eventTimeNs, + const HashableDimensionKey& eventKey, + const long& value) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); + if (eventTimeNs < mCurrentBucketStartTimeNs) { + VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, + (long long)mCurrentBucketStartTimeNs); + return; + } + mCurrentSlicedBucket[eventKey].raw.push_back(make_pair(value, 0)); +} + +void ValueMetricProducer::onMatchedLogEventInternal( + const size_t matcherIndex, const HashableDimensionKey& eventKey, + const map<string, HashableDimensionKey>& conditionKey, bool condition, + const LogEvent& event, bool scheduledPull) { + uint64_t eventTimeNs = event.GetTimestampNs(); + long value = get_value(event); + if (hitGuardRail(eventKey)) { + return; + } + if (mPullTagId != -1) { + onMatchedLogEventInternal_pull(eventTimeNs, eventKey, value, scheduledPull); } else { flushIfNeeded(eventTimeNs); - interval.raw.push_back(make_pair(value, 0)); + onMatchedLogEventInternal_push(eventTimeNs, eventKey, value); } } -long ValueMetricProducer::get_value(const LogEvent& event) { +long ValueMetricProducer::get_value(const LogEvent& event) const { status_t err = NO_ERROR; long val = event.GetLong(mMetric.value_field(), &err); if (err == NO_ERROR) { @@ -298,6 +359,7 @@ long ValueMetricProducer::get_value(const LogEvent& event) { } void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { + std::lock_guard<std::shared_timed_mutex> writeLock(mRWMutex); if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTimeNs) { VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs, (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs)); @@ -346,6 +408,7 @@ void ValueMetricProducer::flushIfNeeded(const uint64_t eventTimeNs) { } size_t ValueMetricProducer::byteSize() const { + std::shared_lock<std::shared_timed_mutex> readLock(mRWMutex); size_t totalSize = 0; for (const auto& pair : mPastBuckets) { totalSize += pair.second.size() * kBucketSize; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 24c76f26dcaa..e87e9dabcec2 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -38,9 +38,9 @@ struct ValueBucket { class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver { public: - ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int pullTagId, - const uint64_t startTimeNs); + ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric, + const int conditionIndex, const sp<ConditionWizard>& wizard, + const int pullTagId, const uint64_t startTimeNs); virtual ~ValueMetricProducer(); @@ -72,18 +72,26 @@ protected: void startNewProtoOutputStream(long long timestamp) override; private: + void onMatchedLogEventInternal_pull(const uint64_t& eventTimeNs, + const HashableDimensionKey& eventKey, const long& value, + bool scheduledPull); + void onMatchedLogEventInternal_push(const uint64_t& eventTimeNs, + const HashableDimensionKey& eventKey, const long& value); + + void SerializeBuckets(); + + bool IsConditionMet() const; + const ValueMetric mMetric; std::shared_ptr<StatsPullerManager> mStatsPullerManager; // for testing - ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex, - const sp<ConditionWizard>& wizard, const int pullTagId, - const uint64_t startTimeNs, + ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric, + const int conditionIndex, const sp<ConditionWizard>& wizard, + const int pullTagId, const uint64_t startTimeNs, std::shared_ptr<StatsPullerManager> statsPullerManager); - Mutex mLock; - // tagId for pulled data. -1 if this is not pulled const int mPullTagId; @@ -102,7 +110,9 @@ private: // TODO: Add a lock to mPastBuckets. std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets; - long get_value(const LogEvent& event); + long get_value(const LogEvent& event) const; + + bool hitGuardRail(const HashableDimensionKey& newKey); static const size_t kBucketSize = sizeof(ValueBucket{}); diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 7ce7f02e06bb..834f7f5b0b86 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -19,6 +19,7 @@ #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" +#include "config/ConfigKey.h" #include "stats_util.h" namespace android { @@ -59,11 +60,14 @@ struct DurationBucket { class DurationTracker { public: - DurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, - int conditionIndex, bool nesting, uint64_t currentBucketStartNs, - uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers, + DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey, + sp<ConditionWizard> wizard, int conditionIndex, bool nesting, + uint64_t currentBucketStartNs, uint64_t bucketSizeNs, + const std::vector<sp<AnomalyTracker>>& anomalyTrackers, std::vector<DurationBucket>& bucket) - : mEventKey(eventKey), + : mConfigKey(key), + mName(name), + mEventKey(eventKey), mWizard(wizard), mConditionTrackerIndex(conditionIndex), mBucketSizeNs(bucketSizeNs), @@ -138,6 +142,10 @@ protected: } } } + // A reference to the DurationMetricProducer's config key. + const ConfigKey& mConfigKey; + + const std::string mName; HashableDimensionKey mEventKey; diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index 06e743daa090..4b346dd25050 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -18,24 +18,50 @@ #include "Log.h" #include "MaxDurationTracker.h" +#include "guardrail/StatsdStats.h" namespace android { namespace os { namespace statsd { -MaxDurationTracker::MaxDurationTracker(const HashableDimensionKey& eventKey, +MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const string& name, + const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers, std::vector<DurationBucket>& bucket) - : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, - anomalyTrackers, bucket) { + : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, + bucketSizeNs, anomalyTrackers, bucket) { +} + +bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { + // ===========GuardRail============== + if (mInfos.find(newKey) != mInfos.end()) { + // if the key existed, we are good! + return false; + } + // 1. Report the tuple count if the tuple count > soft limit + if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mInfos.size() + 1; + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey, + newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("MaxDurTracker %s dropping data for dimension key %s", mName.c_str(), + newKey.c_str()); + return true; + } + } + return false; } void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) { - flushIfNeeded(eventTime); // this will construct a new DurationInfo if this key didn't exist. + if (hitGuardRail(key)) { + return; + } + DurationInfo& duration = mInfos[key]; duration.conditionKeys = conditionKey; VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition); @@ -63,7 +89,6 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime, bool forceStop) { - flushIfNeeded(eventTime); declareAnomalyIfAlarmExpired(eventTime); VLOG("MaxDuration: key %s stop", key.c_str()); if (mInfos.find(key) == mInfos.end()) { @@ -85,7 +110,6 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_ (long long)duration.lastStartTime, (long long)eventTime, (long long)durationTime); duration.lastDuration = duration.lastDuration + durationTime; - duration.lastStartTime = -1; VLOG(" record duration: %lld ", (long long)duration.lastDuration); } break; @@ -223,7 +247,6 @@ void MaxDurationTracker::onConditionChanged(bool condition, const uint64_t times void MaxDurationTracker::noteConditionChanged(const HashableDimensionKey& key, bool conditionMet, const uint64_t timestamp) { - flushIfNeeded(timestamp); declareAnomalyIfAlarmExpired(timestamp); auto it = mInfos.find(key); if (it == mInfos.end()) { diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index 0f79ffed4ec2..e0d1466c6fc4 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -28,7 +28,8 @@ namespace statsd { // they stop or bucket expires. class MaxDurationTracker : public DurationTracker { public: - MaxDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, + MaxDurationTracker(const ConfigKey& key, const string& name, + const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers, @@ -53,6 +54,9 @@ private: void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet, const uint64_t timestamp); + // return true if we should not allow newKey to be tracked because we are above the threshold + bool hitGuardRail(const HashableDimensionKey& newKey); + FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration); FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary); FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition); diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index 29b6c8965ee6..abdfbc06d7a7 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -16,6 +16,7 @@ #define DEBUG true #include "Log.h" #include "OringDurationTracker.h" +#include "guardrail/StatsdStats.h" namespace android { namespace os { @@ -23,22 +24,45 @@ namespace statsd { using std::pair; -OringDurationTracker::OringDurationTracker(const HashableDimensionKey& eventKey, +OringDurationTracker::OringDurationTracker(const ConfigKey& key, const string& name, + const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers, std::vector<DurationBucket>& bucket) - : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, - anomalyTrackers, bucket), + : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, + bucketSizeNs, anomalyTrackers, bucket), mStarted(), mPaused() { mLastStartTime = 0; } +bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { + // ===========GuardRail============== + // 1. Report the tuple count if the tuple count > soft limit + if (mConditionKeyMap.find(newKey) != mConditionKeyMap.end()) { + return false; + } + if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) { + size_t newTupleCount = mConditionKeyMap.size() + 1; + StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey, + newTupleCount); + // 2. Don't add more tuples, we are above the allowed threshold. Drop the data. + if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) { + ALOGE("OringDurTracker %s dropping data for dimension key %s", mName.c_str(), + newKey.c_str()); + return true; + } + } + return false; +} + void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) { - flushIfNeeded(eventTime); + if (hitGuardRail(key)) { + return; + } if (condition) { if (mStarted.size() == 0) { mLastStartTime = eventTime; @@ -59,7 +83,6 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp, const bool stopAll) { - flushIfNeeded(timestamp); declareAnomalyIfAlarmExpired(timestamp); VLOG("Oring: %s stop", key.c_str()); auto it = mStarted.find(key); @@ -72,7 +95,6 @@ void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint6 if (mStarted.empty()) { mDuration += (timestamp - mLastStartTime); detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration); - mLastStartTime = -1; VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime, (long long)mDuration); } @@ -92,7 +114,6 @@ void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint6 } void OringDurationTracker::noteStopAll(const uint64_t timestamp) { - flushIfNeeded(timestamp); declareAnomalyIfAlarmExpired(timestamp); if (!mStarted.empty()) { mDuration += (timestamp - mLastStartTime); @@ -105,7 +126,6 @@ void OringDurationTracker::noteStopAll(const uint64_t timestamp) { mStarted.clear(); mPaused.clear(); mConditionKeyMap.clear(); - mLastStartTime = -1; } bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { @@ -122,7 +142,6 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { // Process the current bucket. if (mStarted.size() > 0) { mDuration += (current_info.mBucketEndNs - mLastStartTime); - mLastStartTime = current_info.mBucketEndNs; } if (mDuration > 0) { current_info.mDuration = mDuration; @@ -138,17 +157,15 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs; info.mBucketNum = mCurrentBucketNum + i; info.mDuration = mBucketSizeNs; - mLastStartTime = info.mBucketEndNs; - if (info.mDuration > 0) { mBucket.push_back(info); addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); VLOG(" add filling bucket with duration %lld", (long long)info.mDuration); - } } } mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs; mCurrentBucketNum += numBucketsForward; + mLastStartTime = mCurrentBucketStartTimeNs; mDuration = 0; // if all stopped, then tell owner it's safe to remove this tracker. @@ -156,7 +173,6 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { } void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) { - flushIfNeeded(timestamp); declareAnomalyIfAlarmExpired(timestamp); vector<pair<HashableDimensionKey, int>> startedToPaused; vector<pair<HashableDimensionKey, int>> pausedToStarted; @@ -179,8 +195,7 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) } if (mStarted.empty()) { - mDuration = (timestamp - mLastStartTime); - mLastStartTime = -1; + mDuration += (timestamp - mLastStartTime); VLOG("Duration add %lld , to %lld ", (long long)(timestamp - mLastStartTime), (long long)mDuration); detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration); @@ -222,13 +237,12 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) } void OringDurationTracker::onConditionChanged(bool condition, const uint64_t timestamp) { - flushIfNeeded(timestamp); declareAnomalyIfAlarmExpired(timestamp); if (condition) { if (!mPaused.empty()) { VLOG("Condition true, all started"); if (mStarted.empty()) { - mLastStartTime = -1; + mLastStartTime = timestamp; } if (mStarted.empty() && !mPaused.empty()) { startAnomalyAlarm(timestamp); @@ -239,8 +253,7 @@ void OringDurationTracker::onConditionChanged(bool condition, const uint64_t tim } else { if (!mStarted.empty()) { VLOG("Condition false, all paused"); - mDuration = (timestamp - mLastStartTime); - mLastStartTime = -1; + mDuration += (timestamp - mLastStartTime); mPaused.insert(mStarted.begin(), mStarted.end()); mStarted.clear(); detectAndDeclareAnomaly(timestamp, mCurrentBucketNum, mDuration); @@ -280,12 +293,12 @@ int64_t OringDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& an pastNs += currRemainingBucketSizeNs; // Now deal with the past buckets, starting with the oldest. - for (int futBucketIdx = 0; futBucketIdx < anomalyTracker.getNumOfPastPackets(); + for (int futBucketIdx = 0; futBucketIdx < anomalyTracker.getNumOfPastBuckets(); futBucketIdx++) { // We now overwrite the oldest bucket with the previous 'current', and start a new // 'current'. pastNs -= anomalyTracker.getPastBucketValue( - mEventKey, mCurrentBucketNum - anomalyTracker.getNumOfPastPackets() + futBucketIdx); + mEventKey, mCurrentBucketNum - anomalyTracker.getNumOfPastBuckets() + futBucketIdx); leftNs = thresholdNs - pastNs; if (leftNs <= mBucketSizeNs) { // Predict anomaly will occur in this bucket. return eventTimestampNs + currRemainingBucketSizeNs + (futBucketIdx * mBucketSizeNs) + diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index ef32fdb510d1..a8404a961dd2 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -27,7 +27,8 @@ namespace statsd { // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted. class OringDurationTracker : public DurationTracker { public: - OringDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, + OringDurationTracker(const ConfigKey& key, const string& name, + const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers, @@ -58,6 +59,9 @@ private: int64_t mLastStartTime; std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap; + // return true if we should not allow newKey to be tracked because we are above the threshold + bool hitGuardRail(const HashableDimensionKey& newKey); + FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap); FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary); FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 466026350ddd..9e5163fe9db5 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#define DEBUG true // STOPSHIP if true +#include "Log.h" + #include "../condition/CombinationConditionTracker.h" #include "../condition/SimpleConditionTracker.h" #include "../external/StatsPullerManager.h" @@ -134,7 +137,8 @@ bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& log return true; } -bool initConditions(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap, +bool initConditions(const ConfigKey& key, const StatsdConfig& config, + const unordered_map<string, int>& logTrackerMap, unordered_map<string, int>& conditionTrackerMap, vector<sp<ConditionTracker>>& allConditionTrackers, unordered_map<int, std::vector<int>>& trackerToConditionMap) { @@ -149,7 +153,7 @@ bool initConditions(const StatsdConfig& config, const unordered_map<string, int> switch (condition.contents_case()) { case Condition::ContentsCase::kSimpleCondition: { allConditionTrackers.push_back(new SimpleConditionTracker( - condition.name(), index, condition.simple_condition(), logTrackerMap)); + key, condition.name(), index, condition.simple_condition(), logTrackerMap)); break; } case Condition::ContentsCase::kCombination: { @@ -184,7 +188,8 @@ bool initConditions(const StatsdConfig& config, const unordered_map<string, int> return true; } -bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap, +bool initMetrics(const ConfigKey& key, const StatsdConfig& config, + const unordered_map<string, int>& logTrackerMap, const unordered_map<string, int>& conditionTrackerMap, const vector<sp<LogMatchingTracker>>& allLogEntryMatchers, vector<sp<ConditionTracker>>& allConditionTrackers, @@ -219,9 +224,12 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l int conditionIndex = -1; if (metric.has_condition()) { - handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap); + bool good = handleMetricWithConditions( + metric.condition(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (!good) { + return false; + } } else { if (metric.links_size() > 0) { ALOGW("metrics has a EventConditionLink but doesn't have a condition"); @@ -230,7 +238,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l } sp<MetricProducer> countProducer = - new CountMetricProducer(metric, conditionIndex, wizard, startTimeNs); + new CountMetricProducer(key, metric, conditionIndex, wizard, startTimeNs); allMetricProducers.push_back(countProducer); } @@ -287,9 +295,12 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l int conditionIndex = -1; if (metric.has_condition()) { - handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap); + bool good = handleMetricWithConditions( + metric.condition(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (!good) { + return false; + } } else { if (metric.links_size() > 0) { ALOGW("metrics has a EventConditionLink but doesn't have a condition"); @@ -298,8 +309,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l } sp<MetricProducer> durationMetric = new DurationMetricProducer( - metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2], - nesting, wizard, internalDimension, startTimeNs); + key, metric, conditionIndex, trackerIndices[0], trackerIndices[1], + trackerIndices[2], nesting, wizard, internalDimension, startTimeNs); allMetricProducers.push_back(durationMetric); } @@ -321,9 +332,12 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l int conditionIndex = -1; if (metric.has_condition()) { - handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap); + bool good = handleMetricWithConditions( + metric.condition(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (!good) { + return false; + } } else { if (metric.links_size() > 0) { ALOGW("metrics has a EventConditionLink but doesn't have a condition"); @@ -332,7 +346,7 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l } sp<MetricProducer> eventMetric = - new EventMetricProducer(metric, conditionIndex, wizard, startTimeNs); + new EventMetricProducer(key, metric, conditionIndex, wizard, startTimeNs); allMetricProducers.push_back(eventMetric); } @@ -368,9 +382,12 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l int conditionIndex = -1; if (metric.has_condition()) { - handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap); + bool good = handleMetricWithConditions( + metric.condition(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (!good) { + return false; + } } else { if (metric.links_size() > 0) { ALOGW("metrics has a EventConditionLink but doesn't have a condition"); @@ -378,8 +395,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l } } - sp<MetricProducer> valueProducer = - new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs); + sp<MetricProducer> valueProducer = new ValueMetricProducer(key, metric, conditionIndex, + wizard, pullTagId, startTimeNs); allMetricProducers.push_back(valueProducer); } @@ -414,9 +431,12 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l int conditionIndex = -1; if (metric.has_condition()) { - handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, - metric.links(), allConditionTrackers, conditionIndex, - conditionToMetricMap); + bool good = handleMetricWithConditions( + metric.condition(), metricIndex, conditionTrackerMap, metric.links(), + allConditionTrackers, conditionIndex, conditionToMetricMap); + if (!good) { + return false; + } } else { if (metric.links_size() > 0) { ALOGW("metrics has a EventConditionLink but doesn't have a condition"); @@ -424,8 +444,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l } } - sp<MetricProducer> gaugeProducer = - new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs); + sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(key, metric, conditionIndex, + wizard, pullTagId, startTimeNs); allMetricProducers.push_back(gaugeProducer); } return true; @@ -442,16 +462,31 @@ bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& me alert.metric_name().c_str()); return false; } + if (alert.trigger_if_sum_gt() < 0 || alert.number_of_buckets() <= 0) { + ALOGW("invalid alert: threshold=%lld num_buckets= %d", + alert.trigger_if_sum_gt(), alert.number_of_buckets()); + return false; + } const int metricIndex = itr->second; - sp<AnomalyTracker> anomalyTracker = - new AnomalyTracker(alert, allMetricProducers[metricIndex]->getBuckeSizeInNs()); + if (alert.trigger_if_sum_gt() > + (int64_t) alert.number_of_buckets() + * allMetricProducers[metricIndex]->getBuckeSizeInNs()) { + ALOGW("invalid alert: threshold (%lld) > possible recordable value (%d x %lld)", + alert.trigger_if_sum_gt(), alert.number_of_buckets(), + (long long) allMetricProducers[metricIndex]->getBuckeSizeInNs()); + return false; + } + + // TODO: Give each MetricProducer a method called createAnomalyTracker(alert), which + // creates either an AnomalyTracker or a DurationAnomalyTracker and returns it. + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); allMetricProducers[metricIndex]->addAnomalyTracker(anomalyTracker); allAnomalyTrackers.push_back(anomalyTracker); } return true; } -bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds, +bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, set<int>& allTagIds, vector<sp<LogMatchingTracker>>& allLogEntryMatchers, vector<sp<ConditionTracker>>& allConditionTrackers, vector<sp<MetricProducer>>& allMetricProducers, @@ -469,13 +504,13 @@ bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds, } ALOGD("initLogMatchingTrackers succeed..."); - if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers, + if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers, trackerToConditionMap)) { ALOGE("initConditionTrackers failed"); return false; } - if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers, + if (!initMetrics(key, config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers, allConditionTrackers, allMetricProducers, conditionToMetricMap, trackerToMetricMap, metricProducerMap)) { ALOGE("initMetricProducers failed"); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 7d7e0c36951f..e7cbd533b5b4 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -36,6 +36,7 @@ namespace statsd { // Initialize the LogMatchingTrackers. // input: +// [key]: the config key that this config belongs to // [config]: the input StatsdConfig // output: // [logTrackerMap]: this map should contain matcher name to index mapping @@ -48,6 +49,7 @@ bool initLogTrackers(const StatsdConfig& config, // Initialize ConditionTrackers // input: +// [key]: the config key that this config belongs to // [config]: the input config // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. // output: @@ -55,7 +57,7 @@ bool initLogTrackers(const StatsdConfig& config, // [allConditionTrackers]: stores the sp to all the ConditionTrackers // [trackerToConditionMap]: contain the mapping from index of // log tracker to condition trackers that use the log tracker -bool initConditions(const StatsdConfig& config, +bool initConditions(const ConfigKey& key, const StatsdConfig& config, const std::unordered_map<std::string, int>& logTrackerMap, std::unordered_map<std::string, int>& conditionTrackerMap, std::vector<sp<ConditionTracker>>& allConditionTrackers, @@ -64,6 +66,7 @@ bool initConditions(const StatsdConfig& config, // Initialize MetricProducers. // input: +// [key]: the config key that this config belongs to // [config]: the input config // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. // [conditionTrackerMap]: condition name to index mapping @@ -73,7 +76,8 @@ bool initConditions(const StatsdConfig& config, // the list of MetricProducer index // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index. bool initMetrics( - const StatsdConfig& config, const std::unordered_map<std::string, int>& logTrackerMap, + const ConfigKey& key, const StatsdConfig& config, + const std::unordered_map<std::string, int>& logTrackerMap, const std::unordered_map<std::string, int>& conditionTrackerMap, const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks, const vector<sp<LogMatchingTracker>>& allLogEntryMatchers, @@ -84,7 +88,7 @@ bool initMetrics( // Initialize MetricsManager from StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. -bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds, +bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, std::set<int>& allTagIds, std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers, std::vector<sp<ConditionTracker>>& allConditionTrackers, std::vector<sp<MetricProducer>>& allMetricProducers, diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 4783cd893ae5..e7e1d43974af 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -172,3 +172,52 @@ message ConfigMetricsReportList { repeated ConfigMetricsReport reports = 2; } + +message StatsdStatsReport { + optional int32 stats_begin_time_sec = 1; + + optional int32 stats_end_time_sec = 2; + + message MatcherStats { + optional string name = 1; + optional int32 matched_times = 2; + } + + message ConditionStats { + optional string name = 1; + optional int32 max_tuple_counts = 2; + } + + message MetricStats { + optional string name = 1; + optional int32 max_tuple_counts = 2; + } + + message ConfigStats { + optional int32 uid = 1; + optional string name = 2; + optional int32 creation_time_sec = 3; + optional int32 deletion_time_sec = 4; + optional int32 metric_count = 5; + optional int32 condition_count = 6; + optional int32 matcher_count = 7; + optional int32 alert_count = 8; + optional bool is_valid = 9; + + repeated int32 broadcast_sent_time_sec = 10; + repeated int32 data_drop_time_sec = 11; + repeated int32 dump_report_time_sec = 12; + repeated MatcherStats matcher_stats = 13; + repeated ConditionStats condition_stats = 14; + repeated MetricStats metric_stats = 15; + } + + repeated ConfigStats config_stats = 3; + + message AtomStats { + optional int32 tag = 1; + optional int32 count = 2; + } + + repeated AtomStats atom_stats = 7; +}
\ No newline at end of file diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp index a95e899354c9..62f06a760656 100644 --- a/cmds/statsd/src/storage/StorageManager.cpp +++ b/cmds/statsd/src/storage/StorageManager.cpp @@ -40,7 +40,7 @@ using std::unique_ptr; void StorageManager::writeFile(const char* file, const void* buffer, int numBytes) { int fd = open(file, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); - if (fd != -1) { + if (fd == -1) { VLOG("Attempt to access %s but failed", file); return; } diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp index 618aef6d0847..696fddf003e1 100644 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ b/cmds/statsd/tests/ConfigManager_test.cpp @@ -62,7 +62,8 @@ MATCHER_P(StatsdConfigEq, name, "") { } TEST(ConfigManagerTest, TestFakeConfig) { - auto metricsManager = std::make_unique<MetricsManager>(build_fake_config()); + auto metricsManager = + std::make_unique<MetricsManager>(ConfigKey(0, "test"), build_fake_config()); EXPECT_TRUE(metricsManager->isConfigValid()); } diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 3dd4e70ff49c..5384e0c2b9a9 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -40,6 +40,8 @@ using android::os::statsd::Condition; // TODO: ADD MORE TEST CASES. +const ConfigKey kConfigKey(0, "test"); + StatsdConfig buildGoodConfig() { StatsdConfig config; config.set_name("12345"); @@ -163,6 +165,25 @@ StatsdConfig buildMissingMatchers() { return config; } +StatsdConfig buildMissingCondition() { + StatsdConfig config; + config.set_name("12345"); + + CountMetric* metric = config.add_count_metric(); + metric->set_name("3"); + metric->set_what("SCREEN_EVENT"); + metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); + metric->set_condition("SOME_CONDITION"); + + LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_EVENT"); + + SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->set_tag(2); + + return config; +} + StatsdConfig buildDimensionMetricsWithMultiTags() { StatsdConfig config; config.set_name("12345"); @@ -254,9 +275,9 @@ TEST(MetricsManagerTest, TestGoodConfig) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers, - allMetricProducers, allAnomalyTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap)); + EXPECT_TRUE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers, + allConditionTrackers, allMetricProducers, allAnomalyTrackers, + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); EXPECT_EQ(1u, allMetricProducers.size()); EXPECT_EQ(1u, allAnomalyTrackers.size()); } @@ -272,9 +293,9 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers, - allMetricProducers, allAnomalyTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap)); + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers, + allConditionTrackers, allMetricProducers, allAnomalyTrackers, + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { @@ -288,9 +309,9 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers, - allMetricProducers, allAnomalyTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap)); + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers, + allConditionTrackers, allMetricProducers, allAnomalyTrackers, + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, TestMissingMatchers) { @@ -303,9 +324,24 @@ TEST(MetricsManagerTest, TestMissingMatchers) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers, - allMetricProducers, allAnomalyTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap)); + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers, + allConditionTrackers, allMetricProducers, allAnomalyTrackers, + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); +} + +TEST(MetricsManagerTest, TestMissingCondition) { + StatsdConfig config = buildMissingCondition(); + set<int> allTagIds; + vector<sp<LogMatchingTracker>> allLogEntryMatchers; + vector<sp<ConditionTracker>> allConditionTrackers; + vector<sp<MetricProducer>> allMetricProducers; + std::vector<sp<AnomalyTracker>> allAnomalyTrackers; + unordered_map<int, std::vector<int>> conditionToMetricMap; + unordered_map<int, std::vector<int>> trackerToMetricMap; + unordered_map<int, std::vector<int>> trackerToConditionMap; + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers, + allConditionTrackers, allMetricProducers, allAnomalyTrackers, + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, TestCircleConditionDependency) { @@ -319,9 +355,9 @@ TEST(MetricsManagerTest, TestCircleConditionDependency) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers, - allMetricProducers, allAnomalyTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap)); + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers, + allConditionTrackers, allMetricProducers, allAnomalyTrackers, + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { @@ -335,9 +371,9 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers, - allMetricProducers, allAnomalyTrackers, conditionToMetricMap, - trackerToMetricMap, trackerToConditionMap)); + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers, + allConditionTrackers, allMetricProducers, allAnomalyTrackers, + conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } #else diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index e0200f27316e..65c2a05543b9 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -51,7 +51,7 @@ TEST(AnomalyTrackerTest, TestConsecutiveBuckets) { alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC); alert.set_trigger_if_sum_gt(2); - AnomalyTracker anomalyTracker(alert, bucketSizeNs); + AnomalyTracker anomalyTracker(alert); std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}}); int64_t eventTimestamp0 = 10; @@ -168,7 +168,7 @@ TEST(AnomalyTrackerTest, TestSparseBuckets) { alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC); alert.set_trigger_if_sum_gt(2); - AnomalyTracker anomalyTracker(alert, bucketSizeNs); + AnomalyTracker anomalyTracker(alert); std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}}); std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}}); diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 11fb011fd74e..8b0b6a473768 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -28,6 +28,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse, bool outputSlicedUid) { SimpleCondition simpleCondition; @@ -76,8 +78,8 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { trackerNameIndexMap["SCREEN_TURNED_ON"] = 0; trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1; - SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*tracker index*/, simpleCondition, - trackerNameIndexMap); + SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", 0 /*tracker index*/, + simpleCondition, trackerNameIndexMap); LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -158,8 +160,9 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { trackerNameIndexMap["SCREEN_TURNED_ON"] = 0; trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1; - SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*condition tracker index*/, - simpleCondition, trackerNameIndexMap); + SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", + 0 /*condition tracker index*/, simpleCondition, + trackerNameIndexMap); LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -227,8 +230,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; trackerNameIndexMap["RELEASE_ALL"] = 2; - SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/, - simpleCondition, trackerNameIndexMap); + SimpleConditionTracker conditionTracker(kConfigKey, conditionName, + 0 /*condition tracker index*/, simpleCondition, + trackerNameIndexMap); int uid = 111; LogEvent event(1 /*tagId*/, 0 /*timestamp*/); @@ -238,6 +242,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedCondition) { vector<MatchingState> matcherState; matcherState.push_back(MatchingState::kMatched); matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); vector<sp<ConditionTracker>> allConditions; vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); @@ -308,8 +313,9 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; trackerNameIndexMap["RELEASE_ALL"] = 2; - SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/, - simpleCondition, trackerNameIndexMap); + SimpleConditionTracker conditionTracker(kConfigKey, conditionName, + 0 /*condition tracker index*/, simpleCondition, + trackerNameIndexMap); int uid1 = 111; string uid1_wl1 = "wl1_1"; int uid2 = 222; @@ -322,6 +328,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { vector<MatchingState> matcherState; matcherState.push_back(MatchingState::kMatched); matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); vector<sp<ConditionTracker>> allConditions; vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); vector<bool> changedCache(1, false); @@ -392,8 +399,9 @@ TEST(SimpleConditionTrackerTest, TestStopAll) { trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; trackerNameIndexMap["RELEASE_ALL"] = 2; - SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/, - simpleCondition, trackerNameIndexMap); + SimpleConditionTracker conditionTracker(kConfigKey, conditionName, + 0 /*condition tracker index*/, simpleCondition, + trackerNameIndexMap); int uid1 = 111; int uid2 = 222; diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp new file mode 100644 index 000000000000..b14b52cf595c --- /dev/null +++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp @@ -0,0 +1,232 @@ +// Copyright (C) 2017 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 "src/guardrail/StatsdStats.h" +#include "statslog.h" + +#include <gtest/gtest.h> +#include <vector> + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +using std::vector; + +TEST(StatsdStatsTest, TestValidConfigAdd) { + StatsdStats stats; + string name = "StatsdTest"; + ConfigKey key(0, name); + const int metricsCount = 10; + const int conditionsCount = 20; + const int matchersCount = 30; + const int alertsCount = 10; + stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, + true /*valid config*/); + vector<uint8_t> output; + stats.dumpStats(&output, false /*reset stats*/); + + StatsdStatsReport report; + bool good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + EXPECT_EQ(1, report.config_stats_size()); + const auto& configReport = report.config_stats(0); + EXPECT_EQ(0, configReport.uid()); + EXPECT_EQ(name, configReport.name()); + EXPECT_EQ(metricsCount, configReport.metric_count()); + EXPECT_EQ(conditionsCount, configReport.condition_count()); + EXPECT_EQ(matchersCount, configReport.matcher_count()); + EXPECT_EQ(alertsCount, configReport.alert_count()); + EXPECT_EQ(true, configReport.is_valid()); + EXPECT_FALSE(configReport.has_deletion_time_sec()); +} + +TEST(StatsdStatsTest, TestInvalidConfigAdd) { + StatsdStats stats; + string name = "StatsdTest"; + ConfigKey key(0, name); + const int metricsCount = 10; + const int conditionsCount = 20; + const int matchersCount = 30; + const int alertsCount = 10; + stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, + false /*bad config*/); + vector<uint8_t> output; + stats.dumpStats(&output, false); + + StatsdStatsReport report; + bool good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + EXPECT_EQ(1, report.config_stats_size()); + const auto& configReport = report.config_stats(0); + // The invalid config should be put into icebox with a deletion time. + EXPECT_TRUE(configReport.has_deletion_time_sec()); +} + +TEST(StatsdStatsTest, TestConfigRemove) { + StatsdStats stats; + string name = "StatsdTest"; + ConfigKey key(0, name); + const int metricsCount = 10; + const int conditionsCount = 20; + const int matchersCount = 30; + const int alertsCount = 10; + stats.noteConfigReceived(key, metricsCount, conditionsCount, matchersCount, alertsCount, true); + vector<uint8_t> output; + stats.dumpStats(&output, false); + StatsdStatsReport report; + bool good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + EXPECT_EQ(1, report.config_stats_size()); + const auto& configReport = report.config_stats(0); + EXPECT_FALSE(configReport.has_deletion_time_sec()); + + stats.noteConfigRemoved(key); + stats.dumpStats(&output, false); + good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + EXPECT_EQ(1, report.config_stats_size()); + const auto& configReport2 = report.config_stats(0); + EXPECT_TRUE(configReport2.has_deletion_time_sec()); +} + +TEST(StatsdStatsTest, TestSubStats) { + StatsdStats stats; + ConfigKey key(0, "test"); + stats.noteConfigReceived(key, 2, 3, 4, 5, true); + + stats.noteMatcherMatched(key, "matcher1"); + stats.noteMatcherMatched(key, "matcher1"); + stats.noteMatcherMatched(key, "matcher2"); + + stats.noteConditionDimensionSize(key, "condition1", 250); + stats.noteConditionDimensionSize(key, "condition1", 240); + + stats.noteMetricDimensionSize(key, "metric1", 201); + stats.noteMetricDimensionSize(key, "metric1", 202); + + // broadcast-> 2 + stats.noteBroadcastSent(key); + stats.noteBroadcastSent(key); + + // data drop -> 1 + stats.noteDataDropped(key); + + // dump report -> 3 + stats.noteMetricsReportSent(key); + stats.noteMetricsReportSent(key); + stats.noteMetricsReportSent(key); + + vector<uint8_t> output; + stats.dumpStats(&output, true); // Dump and reset stats + StatsdStatsReport report; + bool good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + EXPECT_EQ(1, report.config_stats_size()); + const auto& configReport = report.config_stats(0); + EXPECT_EQ(2, configReport.broadcast_sent_time_sec_size()); + EXPECT_EQ(1, configReport.data_drop_time_sec_size()); + EXPECT_EQ(3, configReport.dump_report_time_sec_size()); + + EXPECT_EQ(2, configReport.matcher_stats_size()); + + // matcher1 is the first in the list + if (!configReport.matcher_stats(0).name().compare("matcher1")) { + EXPECT_EQ(2, configReport.matcher_stats(0).matched_times()); + EXPECT_EQ(1, configReport.matcher_stats(1).matched_times()); + EXPECT_EQ("matcher2", configReport.matcher_stats(1).name()); + } else { + // matcher1 is the second in the list. + EXPECT_EQ(1, configReport.matcher_stats(0).matched_times()); + EXPECT_EQ("matcher2", configReport.matcher_stats(0).name()); + + EXPECT_EQ(2, configReport.matcher_stats(1).matched_times()); + EXPECT_EQ("matcher1", configReport.matcher_stats(1).name()); + } + + EXPECT_EQ(1, configReport.condition_stats_size()); + EXPECT_EQ("condition1", configReport.condition_stats(0).name()); + EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts()); + + EXPECT_EQ(1, configReport.metric_stats_size()); + EXPECT_EQ("metric1", configReport.metric_stats(0).name()); + EXPECT_EQ(202, configReport.metric_stats(0).max_tuple_counts()); + + // after resetting the stats, some new events come + stats.noteMatcherMatched(key, "matcher99"); + stats.noteConditionDimensionSize(key, "condition99", 300); + stats.noteMetricDimensionSize(key, "metric99", 270); + + // now the config stats should only contain the stats about the new event. + stats.dumpStats(&output, false); + good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + EXPECT_EQ(1, report.config_stats_size()); + const auto& configReport2 = report.config_stats(0); + EXPECT_EQ(1, configReport2.matcher_stats_size()); + EXPECT_EQ("matcher99", configReport2.matcher_stats(0).name()); + EXPECT_EQ(1, configReport2.matcher_stats(0).matched_times()); + + EXPECT_EQ(1, configReport2.condition_stats_size()); + EXPECT_EQ("condition99", configReport2.condition_stats(0).name()); + EXPECT_EQ(300, configReport2.condition_stats(0).max_tuple_counts()); + + EXPECT_EQ(1, configReport2.metric_stats_size()); + EXPECT_EQ("metric99", configReport2.metric_stats(0).name()); + EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts()); +} + +TEST(StatsdStatsTest, TestAtomLog) { + StatsdStats stats; + time_t now = time(nullptr); + // old event, we get it from the stats buffer. should be ignored. + stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, 1000); + + stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1); + stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2); + stats.noteAtomLogged(android::util::DROPBOX_ERROR_CHANGED, now + 3); + // pulled event, should ignore + stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFERRED, now + 4); + + vector<uint8_t> output; + stats.dumpStats(&output, false); + StatsdStatsReport report; + bool good = report.ParseFromArray(&output[0], output.size()); + EXPECT_TRUE(good); + + EXPECT_EQ(2, report.atom_stats_size()); + bool sensorAtomGood = false; + bool dropboxAtomGood = false; + + for (const auto& atomStats : report.atom_stats()) { + if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 2) { + sensorAtomGood = true; + } + if (atomStats.tag() == android::util::DROPBOX_ERROR_CHANGED && atomStats.count() == 1) { + dropboxAtomGood = true; + } + } + + EXPECT_TRUE(dropboxAtomGood); + EXPECT_TRUE(sensorAtomGood); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 35e08af68739..2cbeaaa8601d 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -32,6 +32,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + TEST(CountMetricProducerTest, TestNonDimensionalEvents) { int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; @@ -48,7 +50,7 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard, + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, bucketStartTimeNs); // 2 events in bucket 1. @@ -106,7 +108,7 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(metric, 1, wizard, bucketStartTimeNs); + CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); countProducer.onConditionChanged(true, bucketStartTimeNs); countProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/); @@ -161,7 +163,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue)); - CountMetricProducer countProducer(metric, 1 /*condition tracker index*/, wizard, + CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard, bucketStartTimeNs); countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false); @@ -194,14 +196,14 @@ TEST(CountMetricProducerTest, TestAnomalyDetection) { int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); CountMetric metric; metric.set_name("1"); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard, + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, bucketStartTimeNs); countProducer.addAnomalyTracker(anomalyTracker); diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 18d177cb9173..724ad59eeab6 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -32,6 +32,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + TEST(EventMetricProducerTest, TestNoCondition) { uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; @@ -45,7 +47,7 @@ TEST(EventMetricProducerTest, TestNoCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EventMetricProducer eventProducer(metric, -1 /*-1 meaning no condition*/, wizard, + EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/); @@ -69,7 +71,7 @@ TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs); + EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/); @@ -111,7 +113,7 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue)); - EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs); + EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/); eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index b9e2b8a7752f..85f50081ca1e 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -148,7 +148,7 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { alert.set_metric_name("1"); alert.set_trigger_if_sum_gt(25); alert.set_number_of_buckets(2); - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); gaugeProducer.addAnomalyTracker(anomalyTracker); std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1); diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 9e169bbf10ef..5d47437c486c 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -36,6 +36,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -45,8 +47,8 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}, buckets); tracker.noteStart("1", true, bucketStartTimeNs, key1); // Event starts again. This would not change anything as it already starts. @@ -72,13 +74,14 @@ TEST(MaxDurationTrackerTest, TestStopAll) { uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}, buckets); tracker.noteStart("1", true, bucketStartTimeNs + 1, key1); // Another event starts in this bucket. tracker.noteStart("2", true, bucketStartTimeNs + 20, key1); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40); tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40); EXPECT_TRUE(tracker.mInfos.empty()); EXPECT_EQ(1u, buckets.size()); @@ -99,8 +102,8 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}, buckets); // The event starts. tracker.noteStart("", true, bucketStartTimeNs + 1, key1); @@ -108,20 +111,9 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { // Starts again. Does not change anything. tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1); - // Flushes at early 2nd bucket. The event still does not stop yet. - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration); - - // Flushes at the end of the 2nd bucket. The event still does not stop yet. - tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs)); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration); - EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration); - // The event stops at early 4th bucket. + tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20); tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/); - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 21); EXPECT_EQ(3u, buckets.size()); EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration); EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration); @@ -137,8 +129,8 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs, + bucketSizeNs, {}, buckets); // 2 starts tracker.noteStart("", true, bucketStartTimeNs + 1, key1); @@ -178,33 +170,19 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t durationTimeNs = 2 * 1000; - MaxDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, + bucketSizeNs, {}, buckets); EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); - tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); - tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); - EXPECT_TRUE(tracker.mInfos.empty()); - EXPECT_EQ(6LL, tracker.mDuration); - - tracker.noteStart("2:maps", false, eventStartTimeNs + 3 * bucketSizeNs + 10, key1); - EXPECT_EQ(1u, tracker.mInfos.size()); - for (const auto& itr : tracker.mInfos) { - EXPECT_EQ(DurationState::kPaused, itr.second.state); - EXPECT_EQ(0LL, itr.second.lastDuration); - } - EXPECT_EQ(3u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); - EXPECT_EQ(6ULL, buckets[2].mDuration); + tracker.onSlicedConditionMayChange(eventStartTimeNs + 5); + + tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); + + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1u, buckets.size()); + EXPECT_EQ(5ULL, buckets[0].mDuration); } TEST(MaxDurationTrackerTest, TestAnomalyDetection) { @@ -223,9 +201,9 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs); - MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs, - {anomalyTracker}, buckets); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs, + bucketSizeNs, {anomalyTracker}, buckets); tracker.noteStart("1", true, eventStartTimeNs, key1); tracker.noteStop("1", eventStartTimeNs + 10, false); @@ -233,6 +211,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { EXPECT_EQ(10LL, tracker.mDuration); tracker.noteStart("2", true, eventStartTimeNs + 20, key1); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC); tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false); EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration); EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index f4edffd1af1e..6913c8161345 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -34,6 +34,8 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); + TEST(OringDurationTrackerTest, TestDurationOverlap) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -47,8 +49,8 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, + bucketSizeNs, {}, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -73,8 +75,8 @@ TEST(OringDurationTrackerTest, TestDurationNested) { uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl @@ -99,8 +101,8 @@ TEST(OringDurationTrackerTest, TestStopAll) { uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1); // overlapping wl @@ -125,15 +127,15 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs); tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1); EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs); EXPECT_EQ(2u, buckets.size()); EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); @@ -162,17 +164,52 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, + bucketSizeNs, {}, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); - tracker.onSlicedConditionMayChange(eventStartTimeNs + 2 * bucketSizeNs + 5); - tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + durationTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + 2 * bucketSizeNs + durationTimeNs); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); + tracker.onSlicedConditionMayChange(eventStartTimeNs + 5); + + tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); + + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1u, buckets.size()); + EXPECT_EQ(5ULL, buckets[0].mDuration); +} + +TEST(OringDurationTrackerTest, TestDurationConditionChange2) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + ConditionKey key1; + key1["APP_BACKGROUND"] = "1:maps|"; + + EXPECT_CALL(*wizard, query(_, key1)) + .Times(2) + .WillOnce(Return(ConditionState::kFalse)) + .WillOnce(Return(ConditionState::kTrue)); + + vector<DurationBucket> buckets; + + uint64_t bucketStartTimeNs = 10000000000; + uint64_t eventStartTimeNs = bucketStartTimeNs + 1; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + uint64_t durationTimeNs = 2 * 1000; + + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, + bucketSizeNs, {}, buckets); + + tracker.noteStart("2:maps", true, eventStartTimeNs, key1); + // condition to false; record duration 5n + tracker.onSlicedConditionMayChange(eventStartTimeNs + 5); + // condition to true. + tracker.onSlicedConditionMayChange(eventStartTimeNs + 1000); + // 2nd duration: 1000ns + tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); + + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1u, buckets.size()); + EXPECT_EQ(1005ULL, buckets[0].mDuration); } TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { @@ -190,8 +227,8 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {}, - buckets); + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1); @@ -223,9 +260,9 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs); - OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, - {anomalyTracker}, buckets); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {anomalyTracker}, buckets); // Nothing in the past bucket. tracker.noteStart("", true, eventStartTimeNs, key1); @@ -242,6 +279,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs)); uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10; + tracker.flushIfNeeded(event1StopTimeNs); tracker.noteStop("1", event1StopTimeNs, false); EXPECT_EQ(1u, buckets.size()); EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10, @@ -282,15 +320,14 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1; uint64_t bucketSizeNs = 30 * NS_PER_SEC; - sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs); - OringDurationTracker tracker("event", wizard, 1, true /*nesting*/, bucketStartTimeNs, - bucketSizeNs, {anomalyTracker}, buckets); + sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); + OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true /*nesting*/, + bucketStartTimeNs, bucketSizeNs, {anomalyTracker}, buckets); tracker.noteStart("", true, eventStartTimeNs, key1); tracker.noteStop("", eventStartTimeNs + 10, false); EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1); EXPECT_TRUE(tracker.mStarted.empty()); - EXPECT_EQ(-1LL, tracker.mLastStartTime); EXPECT_EQ(10LL, tracker.mDuration); EXPECT_EQ(0u, tracker.mStarted.size()); @@ -299,6 +336,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); EXPECT_EQ((long long)(51ULL * NS_PER_SEC), (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC)); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25); tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false); EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs)); EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25), diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 1ed3636f4ec4..9d784663de03 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "metrics_test_helper.h" #include "src/metrics/ValueMetricProducer.h" +#include "metrics_test_helper.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -22,11 +22,11 @@ using namespace testing; using android::sp; +using std::make_shared; using std::set; +using std::shared_ptr; using std::unordered_map; using std::vector; -using std::shared_ptr; -using std::make_shared; #ifdef __ANDROID__ @@ -34,6 +34,7 @@ namespace android { namespace os { namespace statsd { +const ConfigKey kConfigKey(0, "test"); /* * Tests pulled atoms with no conditions */ @@ -42,7 +43,7 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucket3StartTimeNs = bucketStartTimeNs + 2*bucketSizeNs; + int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; ValueMetric metric; metric.set_name("1"); @@ -54,12 +55,13 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); // TODO: pending refactor of StatsPullerManager // For now we still need this so that it doesn't do real pulling. - shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>(); + shared_ptr<MockStatsPullerManager> pullerManager = + make_shared<StrictMock<MockStatsPullerManager>>(); EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); - ValueMetricProducer valueProducer(metric, -1 /*-1 meaning no condition*/, wizard,tagId, - bucketStartTimeNs, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + tagId, bucketStartTimeNs, pullerManager); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -144,41 +146,43 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { int tagId = 1; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>(); + shared_ptr<MockStatsPullerManager> pullerManager = + make_shared<StrictMock<MockStatsPullerManager>>(); EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return()); EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; - - int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; - data->clear(); - shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); - event->write(1); - event->write(100); - event->init(); - data->push_back(event); - return true; - })) - .WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) { - int64_t bucketStartTimeNs = 10000000000; - int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; - - int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; - int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; - data->clear(); - shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); - event->write(1); - event->write(120); - event->init(); - data->push_back(event); - return true; - })); - - ValueMetricProducer valueProducer(metric, 1, wizard,tagId, - bucketStartTimeNs, pullerManager); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; + + int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; + int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write(1); + event->write(100); + event->init(); + data->push_back(event); + return true; + })) + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + int64_t bucketStartTimeNs = 10000000000; + int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL; + + int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; + int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); + event->write(1); + event->write(120); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, + pullerManager); valueProducer.onConditionChanged(true, bucketStartTimeNs + 10); @@ -241,10 +245,11 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { int tagId = 1; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>(); + shared_ptr<MockStatsPullerManager> pullerManager = + make_shared<StrictMock<MockStatsPullerManager>>(); - ValueMetricProducer valueProducer(metric, -1, wizard,-1, - bucketStartTimeNs, pullerManager); + ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs, + pullerManager); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index a46b3c72266c..0266131a6c0d 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -283,4 +283,15 @@ public abstract class ActivityManagerInternal { * @param token The IApplicationToken for the activity */ public abstract void setFocusedActivity(IBinder token); + + /** + * Set a uid that is allowed to bypass stopped app switches, launching an app + * whenever it wants. + * + * @param type Type of the caller -- unique string the caller supplies to identify itself + * and disambiguate with other calles. + * @param uid The uid of the app to be allowed, or -1 to clear the uid for this type. + * @param userId The user it is allowed for. + */ + public abstract void setAllowAppSwitches(@NonNull String type, int uid, int userId); } diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 2c1fad1cf140..80399ae65c58 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -40,6 +40,13 @@ import android.view.DisplayInfo; */ @TestApi public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> { + /** + * bounds that can differ from app bounds, which may include things such as insets. + * + * TODO: Investigate combining with {@link mAppBounds}. Can the latter be a product of the + * former? + */ + private Rect mBounds = new Rect(); /** * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of @@ -117,22 +124,26 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu }) public @interface ActivityType {} + /** Bit that indicates that the {@link #mBounds} changed. + * @hide */ + public static final int WINDOW_CONFIG_BOUNDS = 1 << 0; /** Bit that indicates that the {@link #mAppBounds} changed. * @hide */ - public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 0; + public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 1; /** Bit that indicates that the {@link #mWindowingMode} changed. * @hide */ - public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 1; + public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 2; /** Bit that indicates that the {@link #mActivityType} changed. * @hide */ - public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 2; + public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3; /** @hide */ @IntDef(flag = true, value = { + WINDOW_CONFIG_BOUNDS, WINDOW_CONFIG_APP_BOUNDS, WINDOW_CONFIG_WINDOWING_MODE, - WINDOW_CONFIG_ACTIVITY_TYPE, + WINDOW_CONFIG_ACTIVITY_TYPE }) public @interface WindowConfig {} @@ -151,12 +162,14 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mBounds, flags); dest.writeParcelable(mAppBounds, flags); dest.writeInt(mWindowingMode); dest.writeInt(mActivityType); } private void readFromParcel(Parcel source) { + mBounds = source.readParcelable(Rect.class.getClassLoader()); mAppBounds = source.readParcelable(Rect.class.getClassLoader()); mWindowingMode = source.readInt(); mActivityType = source.readInt(); @@ -181,6 +194,19 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu }; /** + * Sets the bounds to the provided {@link Rect}. + * @param rect the new bounds value. + */ + public void setBounds(Rect rect) { + if (rect == null) { + mBounds.setEmpty(); + return; + } + + mBounds.set(rect); + } + + /** * Set {@link #mAppBounds} to the input Rect. * @param rect The rect value to set {@link #mAppBounds} to. * @see #getAppBounds() @@ -212,6 +238,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu return mAppBounds; } + /** @see #setBounds(Rect) */ + public Rect getBounds() { + return mBounds; + } + public void setWindowingMode(@WindowingMode int windowingMode) { mWindowingMode = windowingMode; } @@ -244,6 +275,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu } public void setTo(WindowConfiguration other) { + setBounds(other.mBounds); setAppBounds(other.mAppBounds); setWindowingMode(other.mWindowingMode); setActivityType(other.mActivityType); @@ -258,6 +290,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** @hide */ public void setToDefaults() { setAppBounds(null); + setBounds(null); setWindowingMode(WINDOWING_MODE_UNDEFINED); setActivityType(ACTIVITY_TYPE_UNDEFINED); } @@ -272,6 +305,11 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu */ public @WindowConfig int updateFrom(@NonNull WindowConfiguration delta) { int changed = 0; + // Only allow override if bounds is not empty + if (!delta.mBounds.isEmpty() && !delta.mBounds.equals(mBounds)) { + changed |= WINDOW_CONFIG_BOUNDS; + setBounds(delta.mBounds); + } if (delta.mAppBounds != null && !delta.mAppBounds.equals(mAppBounds)) { changed |= WINDOW_CONFIG_APP_BOUNDS; setAppBounds(delta.mAppBounds); @@ -303,6 +341,10 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu public @WindowConfig long diff(WindowConfiguration other, boolean compareUndefined) { long changes = 0; + if (!mBounds.equals(other.mBounds)) { + changes |= WINDOW_CONFIG_BOUNDS; + } + // Make sure that one of the values is not null and that they are not equal. if ((compareUndefined || other.mAppBounds != null) && mAppBounds != other.mAppBounds @@ -340,6 +382,16 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu n = mAppBounds.bottom - that.mAppBounds.bottom; if (n != 0) return n; } + + n = mBounds.left - that.mBounds.left; + if (n != 0) return n; + n = mBounds.top - that.mBounds.top; + if (n != 0) return n; + n = mBounds.right - that.mBounds.right; + if (n != 0) return n; + n = mBounds.bottom - that.mBounds.bottom; + if (n != 0) return n; + n = mWindowingMode - that.mWindowingMode; if (n != 0) return n; n = mActivityType - that.mActivityType; @@ -367,6 +419,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu if (mAppBounds != null) { result = 31 * result + mAppBounds.hashCode(); } + result = 31 * result + mBounds.hashCode(); + result = 31 * result + mWindowingMode; result = 31 * result + mActivityType; return result; @@ -375,7 +429,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** @hide */ @Override public String toString() { - return "{mAppBounds=" + mAppBounds + return "{ mBounds=" + mBounds + + " mAppBounds=" + mAppBounds + " mWindowingMode=" + windowingModeToString(mWindowingMode) + " mActivityType=" + activityTypeToString(mActivityType) + "}"; } diff --git a/core/java/android/app/admin/ConnectEvent.java b/core/java/android/app/admin/ConnectEvent.java index ffd38e2b8760..f06a9257b7f8 100644 --- a/core/java/android/app/admin/ConnectEvent.java +++ b/core/java/android/app/admin/ConnectEvent.java @@ -32,29 +32,30 @@ import java.net.UnknownHostException; public final class ConnectEvent extends NetworkEvent implements Parcelable { /** The destination IP address. */ - private final String ipAddress; + private final String mIpAddress; /** The destination port number. */ - private final int port; + private final int mPort; /** @hide */ public ConnectEvent(String ipAddress, int port, String packageName, long timestamp) { super(packageName, timestamp); - this.ipAddress = ipAddress; - this.port = port; + this.mIpAddress = ipAddress; + this.mPort = port; } private ConnectEvent(Parcel in) { - this.ipAddress = in.readString(); - this.port = in.readInt(); - this.packageName = in.readString(); - this.timestamp = in.readLong(); + this.mIpAddress = in.readString(); + this.mPort = in.readInt(); + this.mPackageName = in.readString(); + this.mTimestamp = in.readLong(); + this.mId = in.readLong(); } public InetAddress getInetAddress() { try { // ipAddress is already an address, not a host name, no DNS resolution will happen. - return InetAddress.getByName(ipAddress); + return InetAddress.getByName(mIpAddress); } catch (UnknownHostException e) { // Should never happen as we aren't passing a host name. return InetAddress.getLoopbackAddress(); @@ -62,13 +63,13 @@ public final class ConnectEvent extends NetworkEvent implements Parcelable { } public int getPort() { - return port; + return mPort; } @Override public String toString() { - return String.format("ConnectEvent(%s, %d, %d, %s)", ipAddress, port, timestamp, - packageName); + return String.format("ConnectEvent(%s, %d, %d, %s)", mIpAddress, mPort, mTimestamp, + mPackageName); } public static final Parcelable.Creator<ConnectEvent> CREATOR @@ -96,10 +97,10 @@ public final class ConnectEvent extends NetworkEvent implements Parcelable { public void writeToParcel(Parcel out, int flags) { // write parcel token first out.writeInt(PARCEL_TOKEN_CONNECT_EVENT); - out.writeString(ipAddress); - out.writeInt(port); - out.writeString(packageName); - out.writeLong(timestamp); + out.writeString(mIpAddress); + out.writeInt(mPort); + out.writeString(mPackageName); + out.writeLong(mTimestamp); + out.writeLong(mId); } } - diff --git a/core/java/android/app/admin/DnsEvent.java b/core/java/android/app/admin/DnsEvent.java index f84c5b00a135..4ddf13e07344 100644 --- a/core/java/android/app/admin/DnsEvent.java +++ b/core/java/android/app/admin/DnsEvent.java @@ -34,46 +34,47 @@ import java.util.List; public final class DnsEvent extends NetworkEvent implements Parcelable { /** The hostname that was looked up. */ - private final String hostname; + private final String mHostname; /** Contains (possibly a subset of) the IP addresses returned. */ - private final String[] ipAddresses; + private final String[] mIpAddresses; /** * The number of IP addresses returned from the DNS lookup event. May be different from the * length of ipAddresses if there were too many addresses to log. */ - private final int ipAddressesCount; + private final int mIpAddressesCount; /** @hide */ public DnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount, String packageName, long timestamp) { super(packageName, timestamp); - this.hostname = hostname; - this.ipAddresses = ipAddresses; - this.ipAddressesCount = ipAddressesCount; + this.mHostname = hostname; + this.mIpAddresses = ipAddresses; + this.mIpAddressesCount = ipAddressesCount; } private DnsEvent(Parcel in) { - this.hostname = in.readString(); - this.ipAddresses = in.createStringArray(); - this.ipAddressesCount = in.readInt(); - this.packageName = in.readString(); - this.timestamp = in.readLong(); + this.mHostname = in.readString(); + this.mIpAddresses = in.createStringArray(); + this.mIpAddressesCount = in.readInt(); + this.mPackageName = in.readString(); + this.mTimestamp = in.readLong(); + this.mId = in.readLong(); } /** Returns the hostname that was looked up. */ public String getHostname() { - return hostname; + return mHostname; } /** Returns (possibly a subset of) the IP addresses returned. */ public List<InetAddress> getInetAddresses() { - if (ipAddresses == null || ipAddresses.length == 0) { + if (mIpAddresses == null || mIpAddresses.length == 0) { return Collections.emptyList(); } - final List<InetAddress> inetAddresses = new ArrayList<>(ipAddresses.length); - for (final String ipAddress : ipAddresses) { + final List<InetAddress> inetAddresses = new ArrayList<>(mIpAddresses.length); + for (final String ipAddress : mIpAddresses) { try { // ipAddress is already an address, not a host name, no DNS resolution will happen. inetAddresses.add(InetAddress.getByName(ipAddress)); @@ -90,14 +91,14 @@ public final class DnsEvent extends NetworkEvent implements Parcelable { * addresses to log. */ public int getTotalResolvedAddressCount() { - return ipAddressesCount; + return mIpAddressesCount; } @Override public String toString() { - return String.format("DnsEvent(%s, %s, %d, %d, %s)", hostname, - (ipAddresses == null) ? "NONE" : String.join(" ", ipAddresses), - ipAddressesCount, timestamp, packageName); + return String.format("DnsEvent(%s, %s, %d, %d, %s)", mHostname, + (mIpAddresses == null) ? "NONE" : String.join(" ", mIpAddresses), + mIpAddressesCount, mTimestamp, mPackageName); } public static final Parcelable.Creator<DnsEvent> CREATOR @@ -125,11 +126,11 @@ public final class DnsEvent extends NetworkEvent implements Parcelable { public void writeToParcel(Parcel out, int flags) { // write parcel token first out.writeInt(PARCEL_TOKEN_DNS_EVENT); - out.writeString(hostname); - out.writeStringArray(ipAddresses); - out.writeInt(ipAddressesCount); - out.writeString(packageName); - out.writeLong(timestamp); + out.writeString(mHostname); + out.writeStringArray(mIpAddresses); + out.writeInt(mIpAddressesCount); + out.writeString(mPackageName); + out.writeLong(mTimestamp); + out.writeLong(mId); } } - diff --git a/core/java/android/app/admin/NetworkEvent.java b/core/java/android/app/admin/NetworkEvent.java index 2646c3fdba27..947e4fedbb79 100644 --- a/core/java/android/app/admin/NetworkEvent.java +++ b/core/java/android/app/admin/NetworkEvent.java @@ -18,8 +18,8 @@ package android.app.admin; import android.content.pm.PackageManager; import android.os.Parcel; -import android.os.Parcelable; import android.os.ParcelFormatException; +import android.os.Parcelable; /** * An abstract class that represents a network event. @@ -32,10 +32,13 @@ public abstract class NetworkEvent implements Parcelable { static final int PARCEL_TOKEN_CONNECT_EVENT = 2; /** The package name of the UID that performed the query. */ - String packageName; + String mPackageName; /** The timestamp of the event being reported in milliseconds. */ - long timestamp; + long mTimestamp; + + /** The id of the event. */ + long mId; /** @hide */ NetworkEvent() { @@ -44,8 +47,8 @@ public abstract class NetworkEvent implements Parcelable { /** @hide */ NetworkEvent(String packageName, long timestamp) { - this.packageName = packageName; - this.timestamp = timestamp; + this.mPackageName = packageName; + this.mTimestamp = timestamp; } /** @@ -53,7 +56,7 @@ public abstract class NetworkEvent implements Parcelable { * {@link PackageManager#getNameForUid}. */ public String getPackageName() { - return packageName; + return mPackageName; } /** @@ -61,7 +64,20 @@ public abstract class NetworkEvent implements Parcelable { * the time the event was reported and midnight, January 1, 1970 UTC. */ public long getTimestamp() { - return timestamp; + return mTimestamp; + } + + /** @hide */ + public void setId(long id) { + this.mId = id; + } + + /** + * Returns the id of the event, where the id monotonically increases for each event. The id + * is reset when the device reboots, and when network logging is enabled. + */ + public long getId() { + return this.mId; } @Override @@ -95,4 +111,3 @@ public abstract class NetworkEvent implements Parcelable { @Override public abstract void writeToParcel(Parcel out, int flags); } - diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java index c5f227215b17..ddc5760a6b3d 100644 --- a/core/java/android/app/slice/Slice.java +++ b/core/java/android/app/slice/Slice.java @@ -58,6 +58,16 @@ public final class Slice implements Parcelable { public @interface SliceHint{ } /** + * The meta-data key that allows an activity to easily be linked directly to a slice. + * <p> + * An activity can be statically linked to a slice uri by including a meta-data item + * for this key that contains a valid slice uri for the same application declaring + * the activity. + * @hide + */ + public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI"; + + /** * Hint that this content is a title of other content in the slice. This can also indicate that * the content should be used in the shortcut representation of the slice (icon, label, action), * normally this should be indicated by adding the hint on the action containing that content. diff --git a/core/java/android/content/pm/InstantAppInfo.java b/core/java/android/content/pm/InstantAppInfo.java index 67afc928fd78..cb04fc3ce8e1 100644 --- a/core/java/android/content/pm/InstantAppInfo.java +++ b/core/java/android/content/pm/InstantAppInfo.java @@ -18,6 +18,7 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; @@ -31,6 +32,7 @@ import android.os.Parcelable; * * @hide */ +@SystemApi public final class InstantAppInfo implements Parcelable { private final ApplicationInfo mApplicationInfo; diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index ee75fd443052..f468e5d2f92b 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -31,16 +31,10 @@ import java.util.Objects; import java.util.StringJoiner; /** - * Representation of the capabilities of a network. This object serves two - * purposes: - * <ul> - * <li>An expression of the current capabilities of an active network, typically - * expressed through + * Representation of the capabilities of an active network. Instances are + * typically obtained through * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)} * or {@link ConnectivityManager#getNetworkCapabilities(Network)}. - * <li>An expression of the future capabilities of a desired network, typically - * expressed through {@link NetworkRequest}. - * </ul> * <p> * This replaces the old {@link ConnectivityManager#TYPE_MOBILE} method of * network selection. Rather than indicate a need for Wi-Fi because an @@ -79,7 +73,7 @@ public final class NetworkCapabilities implements Parcelable { */ public void clearAll() { mNetworkCapabilities = mTransportTypes = 0; - mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = 0; + mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; mNetworkSpecifier = null; mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; } @@ -359,6 +353,7 @@ public final class NetworkCapabilities implements Parcelable { /** * Sets all the capabilities set on this {@code NetworkCapability} instance. + * This overwrites any existing capabilities. * * @hide */ @@ -582,6 +577,7 @@ public final class NetworkCapabilities implements Parcelable { /** * Sets all the transports set on this {@code NetworkCapability} instance. + * This overwrites any existing transports. * * @hide */ @@ -780,7 +776,7 @@ public final class NetworkCapabilities implements Parcelable { * Signal strength. This is a signed integer, and higher values indicate better signal. * The exact units are bearer-dependent. For example, Wi-Fi uses RSSI. */ - private int mSignalStrength; + private int mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; /** * Sets the signal strength. This is a signed integer, with higher values indicating a stronger diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 25b1705262e8..97ded2d73b60 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -32,7 +33,7 @@ public class NetworkRequest implements Parcelable { * The {@link NetworkCapabilities} that define this request. * @hide */ - public final NetworkCapabilities networkCapabilities; + public final @NonNull NetworkCapabilities networkCapabilities; /** * Identifies the request. NetworkRequests should only be constructed by @@ -307,7 +308,7 @@ public class NetworkRequest implements Parcelable { return 0; } public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(networkCapabilities, flags); + networkCapabilities.writeToParcel(dest, flags); dest.writeInt(legacyType); dest.writeInt(requestId); dest.writeString(type.name()); @@ -315,7 +316,7 @@ public class NetworkRequest implements Parcelable { public static final Creator<NetworkRequest> CREATOR = new Creator<NetworkRequest>() { public NetworkRequest createFromParcel(Parcel in) { - NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null); + NetworkCapabilities nc = NetworkCapabilities.CREATOR.createFromParcel(in); int legacyType = in.readInt(); int requestId = in.readInt(); Type type = Type.valueOf(in.readString()); // IllegalArgumentException if invalid. diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java index 95e3802eeefa..b00cb482e73e 100644 --- a/core/java/android/net/NetworkState.java +++ b/core/java/android/net/NetworkState.java @@ -18,6 +18,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; +import android.util.Slog; /** * Snapshot of network state. @@ -43,6 +44,16 @@ public class NetworkState implements Parcelable { this.network = network; this.subscriberId = subscriberId; this.networkId = networkId; + + // This object is an atomic view of a network, so the various components + // should always agree on roaming state. + if (networkInfo != null && networkCapabilities != null) { + if (networkInfo.isRoaming() == networkCapabilities + .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) { + Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo + + " and " + networkCapabilities); + } + } } public NetworkState(Parcel in) { diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 6ebf3b9a5e9c..02c7bd6459a6 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -19,9 +19,11 @@ package android.os; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.Application; import android.content.Context; import android.text.TextUtils; import android.util.Slog; +import android.view.View; import com.android.internal.telephony.TelephonyProperties; @@ -761,6 +763,80 @@ public class Build { * <p>Applications targeting this or a later release will get these * new changes in behavior:</p> * <ul> + * <li><a href="{@docRoot}about/versions/oreo/background.html">Background execution limits</a> + * are applied to the application.</li> + * <li>The behavior of AccountManager's + * {@link android.accounts.AccountManager#getAccountsByType}, + * {@link android.accounts.AccountManager#getAccountsByTypeAndFeatures}, and + * {@link android.accounts.AccountManager#hasFeatures} has changed as documented there.</li> + * <li>{@link android.app.ActivityManager.RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE_PRE_26} + * is now returned as + * {@link android.app.ActivityManager.RunningAppProcessInfo#IMPORTANCE_PERCEPTIBLE}.</li> + * <li>The {@link android.app.NotificationManager} now requires the use of notification + * channels.</li> + * <li>Changes to the strict mode that are set in + * {@link Application#onCreate Application.onCreate} will no longer be clobbered after + * that function returns.</li> + * <li>A shared library apk with native code will have that native code included in + * the library path of its clients.</li> + * <li>{@link android.content.Context#getSharedPreferences Context.getSharedPreferences} + * in credential encrypted storage will throw an exception before the user is unlocked.</li> + * <li>Attempting to retrieve a {@link Context#FINGERPRINT_SERVICE} on a device that + * does not support that feature will now throw a runtime exception.</li> + * <li>{@link android.app.Fragment} will stop any active view animations when + * the fragment is stopped.</li> + * <li>Some compatibility code in Resources that attempts to use the default Theme + * the app may be using will be turned off, requiring the app to explicitly request + * resources with the right theme.</li> + * <li>{@link android.content.ContentResolver#notifyChange ContentResolver.notifyChange} and + * {@link android.content.ContentResolver#registerContentObserver + * ContentResolver.registerContentObserver} + * will throw a SecurityException if the caller does not have permission to access + * the provider (or the provider doesn't exit); otherwise the call will be silently + * ignored.</li> + * <li>{@link android.hardware.camera2.CameraDevice#createCaptureRequest + * CameraDevice.createCaptureRequest} will enable + * {@link android.hardware.camera2.CaptureRequest#CONTROL_ENABLE_ZSL} by default for + * still image capture.</li> + * <li>WallpaperManager's {@link android.app.WallpaperManager#getWallpaperFile}, + * {@link android.app.WallpaperManager#getDrawable}, + * {@link android.app.WallpaperManager#getFastDrawable}, + * {@link android.app.WallpaperManager#peekDrawable}, and + * {@link android.app.WallpaperManager#peekFastDrawable} will throw an exception + * if you can not access the wallpaper.</li> + * <li>The behavior of + * {@link android.hardware.usb.UsbDeviceConnection#requestWait UsbDeviceConnection.requestWait} + * is modified as per the documentation there.</li> + * <li>{@link StrictMode.VmPolicy.Builder#detectAll StrictMode.VmPolicy.Builder.detectAll} + * will also enable {@link StrictMode.VmPolicy.Builder#detectContentUriWithoutPermission} + * and {@link StrictMode.VmPolicy.Builder#detectUntaggedSockets}.</li> + * <li>{@link StrictMode.ThreadPolicy.Builder#detectAll StrictMode.ThreadPolicy.Builder.detectAll} + * will also enable {@link StrictMode.ThreadPolicy.Builder#detectUnbufferedIo}.</li> + * <li>{@link android.provider.DocumentsContract}'s various methods will throw failure + * exceptions back to the caller instead of returning null. + * <li>{@link View#hasFocusable View.hasFocusable} now includes auto-focusable views.</li> + * <li>{@link android.view.SurfaceView} will no longer always change the underlying + * Surface object when something about it changes; apps need to look at the current + * state of the object to determine which things they are interested in have changed.</li> + * <li>{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY} must be + * used for overlay windows, other system overlay window types are not allowed.</li> + * <li>{@link android.view.ViewTreeObserver#addOnDrawListener + * ViewTreeObserver.addOnDrawListener} will throw an exception if called from within + * onDraw.</li> + * <li>{@link android.graphics.Canvas#setBitmap Canvas.setBitmap} will no longer preserve + * the current matrix and clip stack of the canvas.</li> + * <li>{@link android.widget.ListPopupWindow#setHeight ListPopupWindow.setHeight} + * will throw an exception if a negative height is supplied.</li> + * <li>{@link android.widget.TextView} will use internationalized input for numbers, + * dates, and times.</li> + * <li>{@link android.widget.Toast} must be used for showing toast windows; the toast + * window type can not be directly used.</li> + * <li>{@link android.net.wifi.WifiManager#getConnectionInfo WifiManager.getConnectionInfo} + * requires that the caller hold the location permission to return BSSID/SSID</li> + * <li>{@link android.net.wifi.p2p.WifiP2pManager#requestPeers WifiP2pManager.requestPeers} + * requires the caller hold the location permission.</li> + * <li>{@link android.R.attr#maxAspectRatio} defaults to 0, meaning there is no restriction + * on the app's maximum aspect ratio (so it can be stretched to fill larger screens).</li> * <li>{@link android.R.attr#focusable} defaults to a new state ({@code auto}) where it will * inherit the value of {@link android.R.attr#clickable} unless explicitly overridden.</li> * <li>A default theme-appropriate focus-state highlight will be supplied to all Views @@ -772,6 +848,15 @@ public class Build { /** * O MR1. + * + * <p>Applications targeting this or a later release will get these + * new changes in behavior:</p> + * <ul> + * <li>Apps exporting and linking to apk shared libraries must explicitly + * enumerate all signing certificates in a consistent order.</li> + * <li>{@link android.R.attr#screenOrientation} can not be used to request a fixed + * orientation if the associated activity is not fullscreen and opaque.</li> + * </ul> */ public static final int O_MR1 = 27; diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 36121d452d96..62bb38540e44 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -20,6 +20,7 @@ import android.annotation.Nullable; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.ExceptionUtils; import android.util.Log; import android.util.Size; import android.util.SizeF; @@ -1866,7 +1867,14 @@ public final class Parcel { if (remoteStackTrace != null) { RemoteException cause = new RemoteException( "Remote stack trace:\n" + remoteStackTrace, null, false, false); - e.initCause(cause); + try { + Throwable rootCause = ExceptionUtils.getRootCause(e); + if (rootCause != null) { + rootCause.initCause(cause); + } + } catch (RuntimeException ex) { + Log.e(TAG, "Cannot set cause " + cause + " for " + e, ex); + } } SneakyThrow.sneakyThrow(e); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 994575545c5c..729c0ff6febb 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9379,6 +9379,9 @@ public final class Settings { /** {@hide} */ public static final String BLUETOOTH_PAN_PRIORITY_PREFIX = "bluetooth_pan_priority_"; + /** {@hide} */ + public static final String + BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_"; /** * Activity manager specific settings. @@ -9745,6 +9748,14 @@ public final class Settings { } /** + * Get the key that retrieves a bluetooth hearing aid priority. + * @hide + */ + public static final String getBluetoothHearingAidPriorityKey(String address) { + return BLUETOOTH_HEARING_AID_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); + } + + /** * Get the key that retrieves a bluetooth map priority. * @hide */ diff --git a/core/java/android/service/autofill/ISaveCallback.aidl b/core/java/android/service/autofill/ISaveCallback.aidl index e260c7375cc5..a9364fe5ccba 100644 --- a/core/java/android/service/autofill/ISaveCallback.aidl +++ b/core/java/android/service/autofill/ISaveCallback.aidl @@ -16,12 +16,14 @@ package android.service.autofill; +import android.content.IntentSender; + /** * Interface to receive the result of a save request. * * @hide */ interface ISaveCallback { - void onSuccess(); + void onSuccess(in IntentSender intentSender); void onFailure(CharSequence message); } diff --git a/core/java/android/service/autofill/NegationValidator.java b/core/java/android/service/autofill/NegationValidator.java new file mode 100644 index 000000000000..a963f9f94346 --- /dev/null +++ b/core/java/android/service/autofill/NegationValidator.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.service.autofill; + +import static android.view.autofill.Helper.sDebug; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +/** + * Validator used to implement a {@code NOT} logical operation. + * + * @hide + */ +final class NegationValidator extends InternalValidator { + @NonNull private final InternalValidator mValidator; + + NegationValidator(@NonNull InternalValidator validator) { + mValidator = Preconditions.checkNotNull(validator); + } + + @Override + public boolean isValid(@NonNull ValueFinder finder) { + return !mValidator.isValid(finder); + } + + ///////////////////////////////////// + // Object "contract" methods. // + ///////////////////////////////////// + @Override + public String toString() { + if (!sDebug) return super.toString(); + + return "NegationValidator: [validator=" + mValidator + "]"; + } + + ///////////////////////////////////// + // Parcelable "contract" methods. // + ///////////////////////////////////// + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mValidator, flags); + } + + public static final Parcelable.Creator<NegationValidator> CREATOR = + new Parcelable.Creator<NegationValidator>() { + @Override + public NegationValidator createFromParcel(Parcel parcel) { + return new NegationValidator(parcel.readParcelable(null)); + } + + @Override + public NegationValidator[] newArray(int size) { + return new NegationValidator[size]; + } + }; +} diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java index 7207f1df3ee5..855981a544fd 100644 --- a/core/java/android/service/autofill/SaveCallback.java +++ b/core/java/android/service/autofill/SaveCallback.java @@ -16,9 +16,14 @@ package android.service.autofill; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Activity; +import android.content.IntentSender; import android.os.RemoteException; +import com.android.internal.util.Preconditions; + /** * Handles save requests from the {@link AutofillService} into the {@link Activity} being * autofilled. @@ -36,18 +41,33 @@ public final class SaveCallback { * Notifies the Android System that an * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled * by the service. + */ + public void onSuccess() { + onSuccessInternal(null); + } + + /** + * Notifies the Android System that an + * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully handled + * by the service. * - * <p>If the service could not handle the request right away—for example, because it must - * launch an activity asking the user to authenticate first or because the network is - * down—it should still call {@link #onSuccess()}. + * <p>This method is useful when the service requires extra work—for example, launching an + * activity asking the user to authenticate first —before it can process the request, + * as the intent will be launched from the context of the activity being autofilled and hence + * will be part of that activity's stack. * - * @throws RuntimeException if an error occurred while calling the Android System. + * @param intentSender intent that will be launched from the context of activity being + * autofilled. */ - public void onSuccess() { + public void onSuccess(@NonNull IntentSender intentSender) { + onSuccessInternal(Preconditions.checkNotNull(intentSender)); + } + + private void onSuccessInternal(@Nullable IntentSender intentSender) { assertNotCalled(); mCalled = true; try { - mCallback.onSuccess(); + mCallback.onSuccess(intentSender); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } @@ -63,11 +83,10 @@ public final class SaveCallback { * the {@link SaveRequest} and call {@link #onSuccess()} instead. * * <p><b>Note:</b> The Android System displays an UI with the supplied error message; if - * you prefer to show your own message, call {@link #onSuccess()} instead. + * you prefer to show your own message, call {@link #onSuccess()} or + * {@link #onSuccess(IntentSender)} instead. * * @param message error message to be displayed to the user. - * - * @throws RuntimeException if an error occurred while calling the Android System. */ public void onFailure(CharSequence message) { assertNotCalled(); diff --git a/core/java/android/service/autofill/Validators.java b/core/java/android/service/autofill/Validators.java index 51b503c21690..1c8386870fe7 100644 --- a/core/java/android/service/autofill/Validators.java +++ b/core/java/android/service/autofill/Validators.java @@ -52,6 +52,19 @@ public final class Validators { return new OptionalValidators(getInternalValidators(validators)); } + /** + * Creates a validator that is valid only if {@code validator} is not. + * + * @throws IllegalArgumentException if {@code validator} is an instance of a class that is not + * provided by the Android System. + */ + @NonNull + public static Validator not(@NonNull Validator validator) { + Preconditions.checkArgument(validator instanceof InternalValidator, + "validator not provided by Android System: " + validator); + return new NegationValidator((InternalValidator) validator); + } + private static InternalValidator[] getInternalValidators(Validator[] validators) { Preconditions.checkArrayElementsNotNull(validators, "validators"); diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 5990d7be9d23..2e8c27ab7217 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -130,8 +130,10 @@ static void scaleNinePatchChunk(android::Res_png_9patch* chunk, float scale, chunk->paddingRight = int(chunk->paddingRight * scale + 0.5f); chunk->paddingBottom = int(chunk->paddingBottom * scale + 0.5f); - scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth); - scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight); + // The max value for the divRange is one pixel less than the actual max to ensure that the size + // of the last div is not zero. A div of size 0 is considered invalid input and will not render. + scaleDivRange(chunk->getXDivs(), chunk->numXDivs, scale, scaledWidth - 1); + scaleDivRange(chunk->getYDivs(), chunk->numYDivs, scale, scaledHeight - 1); } class ScaleCheckingAllocator : public SkBitmap::HeapAllocator { diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 32ef3dc0aed4..1407ae4c4bfe 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -776,7 +776,10 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( } // Assign system_server to the correct memory cgroup. - if (!WriteStringToFile(StringPrintf("%d", pid), "/dev/memcg/system/tasks")) { + // Not all devices mount /dev/memcg so check for the file first + // to avoid unnecessarily printing errors and denials in the logs. + if (!access("/dev/memcg/system/tasks", F_OK) && + !WriteStringToFile(StringPrintf("%d", pid), "/dev/memcg/system/tasks")) { ALOGE("couldn't write %d to /dev/memcg/system/tasks", pid); } } diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index 5d5aea2a12cd..322914061509 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -281,6 +281,7 @@ message GlobalSettingsProto { optional SettingProto bluetooth_pbap_client_priority_prefix = 209; optional SettingProto bluetooth_sap_priority_prefix = 210; optional SettingProto bluetooth_pan_priority_prefix = 211; + optional SettingProto bluetooth_hearing_aid_priority_prefix = 345; optional SettingProto activity_manager_constants = 317; optional SettingProto device_idle_constants = 212; optional SettingProto device_idle_constants_watch = 213; @@ -387,7 +388,7 @@ message GlobalSettingsProto { optional SettingProto enable_deletion_helper_no_threshold_toggle = 340; optional SettingProto notification_snooze_options = 341; - // Next tag = 345; + // Next tag = 346; } message SecureSettingsProto { diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 999ba7392fab..e4310e1070c8 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4373,7 +4373,7 @@ <string name="package_deleted_device_owner">Deleted by your admin</string> <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description --> - <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string> + <string name="battery_saver_description">To help improve battery life, Battery Saver reduces your device’s performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery Saver turns off automatically when your device is charging.</string> <!-- [CHAR_LIMIT=NONE] Data saver: Feature description --> <string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 51bfc209bf55..3f2a46a0e9ca 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -68,6 +68,7 @@ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SMS"/> <uses-permission android:name="android.permission.TEST_GRANTED" /> + <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" /> <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" /> <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" /> diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml index 23d70b8dedd4..970a0f00bf5e 100644 --- a/core/tests/coretests/AndroidTest.xml +++ b/core/tests/coretests/AndroidTest.xml @@ -16,6 +16,7 @@ <configuration description="Runs Frameworks Core Tests."> <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> <option name="test-file-name" value="FrameworksCoreTests.apk" /> + <option name="test-file-name" value="BstatsTestApp.apk" /> </target_preparer> <option name="test-suite-tag" value="apct" /> diff --git a/core/tests/coretests/BstatsTestApp/Android.mk b/core/tests/coretests/BstatsTestApp/Android.mk new file mode 100644 index 000000000000..628025751d77 --- /dev/null +++ b/core/tests/coretests/BstatsTestApp/Android.mk @@ -0,0 +1,32 @@ +# Copyright (C) 2017 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := BstatsTestApp +LOCAL_CERTIFICATE := platform +LOCAL_DEX_PREOPT := false +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE)
\ No newline at end of file diff --git a/core/tests/coretests/BstatsTestApp/AndroidManifest.xml b/core/tests/coretests/BstatsTestApp/AndroidManifest.xml new file mode 100644 index 000000000000..0cb5498a9c21 --- /dev/null +++ b/core/tests/coretests/BstatsTestApp/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.coretests.apps.bstatstestapp"> + + <application> + <activity android:name=".TestActivity" + android:exported="true" /> + <service android:name=".IsolatedTestService" + android:exported="true" + android:isolatedProcess="true" /> + </application> + +</manifest>
\ No newline at end of file diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java new file mode 100644 index 000000000000..1f5f39730096 --- /dev/null +++ b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/IsolatedTestService.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 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.coretests.apps.bstatstestapp; + +import com.android.frameworks.coretests.aidl.ICmdReceiver; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.Process; +import android.os.SystemClock; +import android.util.Log; + +public class IsolatedTestService extends Service { + private static final String TAG = IsolatedTestService.class.getName(); + + @Override + public void onCreate() { + Log.d(TAG, "onCreate called. myUid=" + Process.myUid()); + } + + @Override + public IBinder onBind(Intent intent) { + return mReceiver.asBinder(); + } + + private ICmdReceiver mReceiver = new ICmdReceiver.Stub() { + @Override + public void doSomeWork(int durationMs) { + final long endTime = SystemClock.uptimeMillis() + durationMs; + double x; + double y; + double z; + while (SystemClock.uptimeMillis() <= endTime) { + x = 0.02; + x *= 1000; + y = x % 5; + z = Math.sqrt(y / 100); + } + }; + + @Override + public void finishHost() { + stopSelf(); + } + }; +} diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java new file mode 100644 index 000000000000..87b14d934913 --- /dev/null +++ b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestActivity.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 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.coretests.apps.bstatstestapp; + +import com.android.frameworks.coretests.aidl.ICmdCallback; +import com.android.frameworks.coretests.aidl.ICmdReceiver; + +import android.app.Activity; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; + +public class TestActivity extends Activity { + private static final String TAG = TestActivity.class.getName(); + + private static final String EXTRA_KEY_CMD_RECEIVER = "cmd_receiver"; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + Log.d(TAG, "onCreate called"); + notifyActivityLaunched(); + } + + private void notifyActivityLaunched() { + if (getIntent() == null) { + return; + } + + final Bundle extras = getIntent().getExtras(); + if (extras == null) { + return; + } + final ICmdCallback callback = ICmdCallback.Stub.asInterface( + extras.getBinder(EXTRA_KEY_CMD_RECEIVER)); + try { + callback.onActivityLaunched(mReceiver.asBinder()); + } catch (RemoteException e) { + Log.e(TAG, "Error occured while notifying the test: " + e); + } + } + + @Override + public void finish() { + super.finish(); + Log.d(TAG, "finish called"); + } + + private ICmdReceiver mReceiver = new ICmdReceiver.Stub() { + @Override + public void doSomeWork(int durationMs) { + final long endTime = SystemClock.uptimeMillis() + durationMs; + double x; + double y; + double z; + while (SystemClock.uptimeMillis() <= endTime) { + x = 0.02; + x *= 1000; + y = x % 5; + z = Math.sqrt(y / 100); + } + }; + + @Override + public void finishHost() { + finish(); + } + }; +} diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdCallback.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdCallback.aidl new file mode 100644 index 000000000000..53a181acd5b6 --- /dev/null +++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdCallback.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 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.frameworks.coretests.aidl; + +interface ICmdCallback { + void onActivityLaunched(IBinder receiver); +}
\ No newline at end of file diff --git a/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdReceiver.aidl b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdReceiver.aidl new file mode 100644 index 000000000000..c406570ecfae --- /dev/null +++ b/core/tests/coretests/aidl/com/android/frameworks/coretests/aidl/ICmdReceiver.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 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.frameworks.coretests.aidl; + +interface ICmdReceiver { + void doSomeWork(int durationMs); + void finishHost(); +}
\ No newline at end of file diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 492ef7288fcc..454d2096eca8 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -127,6 +127,7 @@ public class SettingsBackupTest { Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX, Settings.Global.BLUETOOTH_SAP_PRIORITY_PREFIX, + Settings.Global.BLUETOOTH_HEARING_AID_PRIORITY_PREFIX, Settings.Global.BOOT_COUNT, Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, diff --git a/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java new file mode 100644 index 000000000000..682a0021b274 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BstatsCpuTimesValidationTest.java @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2017 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.os; + +import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +import static org.junit.Assume.assumeTrue; + +import com.android.frameworks.coretests.aidl.ICmdCallback; +import com.android.frameworks.coretests.aidl.ICmdReceiver; + +import android.app.KeyguardManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.os.BatteryManager; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.Process; +import android.os.SystemClock; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.LargeTest; +import android.support.test.runner.AndroidJUnit4; +import android.support.test.uiautomator.UiDevice; +import android.util.Log; + +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class BstatsCpuTimesValidationTest { + private static final String TAG = BstatsCpuTimesValidationTest.class.getName(); + + private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp"; + private static final String TEST_ACTIVITY = TEST_PKG + ".TestActivity"; + private static final String ISOLATED_TEST_SERVICE = TEST_PKG + ".IsolatedTestService"; + + private static final String EXTRA_KEY_CMD_RECEIVER = "cmd_receiver"; + + private static final int BATTERY_STATE_TIMEOUT_MS = 2000; + private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 200; + + private static final int START_ACTIVITY_TIMEOUT_MS = 2000; + private static final int START_ISOLATED_SERVICE_TIMEOUT_MS = 2000; + + private static final int GENERAL_TIMEOUT_MS = 1000; + private static final int GENERAL_INTERVAL_MS = 100; + + private static final int WORK_DURATION_MS = 2000; + + private static Context sContext; + private static UiDevice sUiDevice; + private static int sTestPkgUid; + private static boolean sCpuFreqTimesAvailable; + + @BeforeClass + public static void setupOnce() throws Exception { + sContext = InstrumentationRegistry.getContext(); + sUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + sContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); + sTestPkgUid = sContext.getPackageManager().getPackageUid(TEST_PKG, 0); + sCpuFreqTimesAvailable = cpuFreqTimesAvailable(); + } + + // Checks cpu freq times of system uid as an indication of whether /proc/uid_time_in_state + // kernel node is available. + private static boolean cpuFreqTimesAvailable() throws Exception { + final long[] cpuTimes = getAllCpuFreqTimes(Process.SYSTEM_UID); + return cpuTimes != null; + } + + @Test + public void testCpuFreqTimes() throws Exception { + assumeTrue(sCpuFreqTimesAvailable); + + batteryOnScreenOn(); + forceStop(); + resetBatteryStats(); + final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); + assertNull("Initial snapshot should be null", initialSnapshot); + doSomeWork(); + forceStop(); + + final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid); + assertCpuTimesValid(cpuTimesMs); + long actualCpuTimeMs = 0; + for (int i = 0; i < cpuTimesMs.length / 2; ++i) { + actualCpuTimeMs += cpuTimesMs[i]; + } + assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualCpuTimeMs); + batteryOffScreenOn(); + } + + @Test + public void testCpuFreqTimes_screenOff() throws Exception { + assumeTrue(sCpuFreqTimesAvailable); + + batteryOnScreenOff(); + forceStop(); + resetBatteryStats(); + final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); + assertNull("Initial snapshot should be null", initialSnapshot); + doSomeWork(); + forceStop(); + + final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid); + assertCpuTimesValid(cpuTimesMs); + long actualTotalCpuTimeMs = 0; + for (int i = 0; i < cpuTimesMs.length / 2; ++i) { + actualTotalCpuTimeMs += cpuTimesMs[i]; + } + assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualTotalCpuTimeMs); + long actualScreenOffCpuTimeMs = 0; + for (int i = cpuTimesMs.length / 2; i < cpuTimesMs.length; ++i) { + actualScreenOffCpuTimeMs += cpuTimesMs[i]; + } + assertApproximateValue("Incorrect screen-off cpu time", + WORK_DURATION_MS, actualScreenOffCpuTimeMs); + batteryOffScreenOn(); + } + + @Test + public void testCpuFreqTimes_isolatedProcess() throws Exception { + assumeTrue(sCpuFreqTimesAvailable); + + batteryOnScreenOn(); + forceStop(); + resetBatteryStats(); + final long[] initialSnapshot = getAllCpuFreqTimes(sTestPkgUid); + assertNull("Initial snapshot should be null", initialSnapshot); + doSomeWorkInIsolatedProcess(); + forceStop(); + + final long[] cpuTimesMs = getAllCpuFreqTimes(sTestPkgUid); + assertCpuTimesValid(cpuTimesMs); + long actualCpuTimeMs = 0; + for (int i = 0; i < cpuTimesMs.length / 2; ++i) { + actualCpuTimeMs += cpuTimesMs[i]; + } + assertApproximateValue("Incorrect total cpu time", WORK_DURATION_MS, actualCpuTimeMs); + batteryOffScreenOn(); + } + + private void assertCpuTimesValid(long[] cpuTimes) { + assertNotNull(cpuTimes); + for (int i = 0; i < cpuTimes.length; ++i) { + if (cpuTimes[i] < 0) { + fail("Malformed cpu times data (-ve values): " + Arrays.toString(cpuTimes)); + } + } + final int numFreqs = cpuTimes.length / 2; + for (int i = 0; i < numFreqs; ++i) { + if (cpuTimes[i] < cpuTimes[numFreqs + i]) { + fail("Malformed cpu times data (screen-off > total)" + Arrays.toString(cpuTimes)); + } + } + } + + private void assertApproximateValue(String errorPrefix, long expectedValue, long actualValue) { + assertValueRange(errorPrefix, actualValue, expectedValue * 0.5, expectedValue * 1.5); + } + + private void assertValueRange(String errorPrefix, + long actualvalue, double minValue, double maxValue) { + final String errorMsg = String.format(errorPrefix + "; actual=%s; min=%s; max=%s", + actualvalue, minValue, maxValue); + assertTrue(errorMsg, actualvalue < maxValue); + assertTrue(errorMsg, actualvalue > minValue); + } + + private void doSomeWork() throws Exception { + final ICmdReceiver receiver = ICmdReceiver.Stub.asInterface(startActivity()); + receiver.doSomeWork(WORK_DURATION_MS); + receiver.finishHost(); + } + + private void doSomeWorkInIsolatedProcess() throws Exception { + final ICmdReceiver receiver = ICmdReceiver.Stub.asInterface(startIsolatedService()); + receiver.doSomeWork(WORK_DURATION_MS); + receiver.finishHost(); + } + + private IBinder startIsolatedService() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final IBinder[] binders = new IBinder[1]; + final ServiceConnection connection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + binders[0] = service; + latch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + } + }; + final Intent launchIntent = new Intent() + .setComponent(new ComponentName(TEST_PKG, ISOLATED_TEST_SERVICE)); + sContext.bindService(launchIntent, connection, Context.BIND_AUTO_CREATE + | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_FOREGROUND); + if (latch.await(START_ISOLATED_SERVICE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + if (binders[0] == null) { + fail("Receiver binder should not be null"); + } + return binders[0]; + } else { + fail("Timed out waiting for the isolated test service to start"); + } + return null; + } + + private IBinder startActivity() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final Intent launchIntent = new Intent() + .setComponent(new ComponentName(TEST_PKG, TEST_ACTIVITY)); + final Bundle extras = new Bundle(); + final IBinder[] binders = new IBinder[1]; + extras.putBinder(EXTRA_KEY_CMD_RECEIVER, new ICmdCallback.Stub() { + @Override + public void onActivityLaunched(IBinder receiver) { + binders[0] = receiver; + latch.countDown(); + } + }); + launchIntent.putExtras(extras); + sContext.startActivity(launchIntent); + if (latch.await(START_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { + if (binders[0] == null) { + fail("Receiver binder should not be null"); + } + return binders[0]; + } else { + fail("Timed out waiting for the test activity to start; testUid=" + sTestPkgUid); + } + return null; + } + + private static long[] getAllCpuFreqTimes(int uid) throws Exception { + final String checkinDump = executeCmdSilent("dumpsys batterystats --checkin"); + final Pattern pattern = Pattern.compile(uid + ",l,ctf,A,(.*?)\n"); + final Matcher matcher = pattern.matcher(checkinDump); + if (!matcher.find()) { + return null; + } + final String[] uidTimesStr = matcher.group(1).split(","); + final int freqCount = Integer.parseInt(uidTimesStr[0]); + if (uidTimesStr.length != (2 * freqCount + 1)) { + fail("Malformed data: " + Arrays.toString(uidTimesStr)); + } + final long[] cpuTimes = new long[freqCount * 2]; + for (int i = 0; i < cpuTimes.length; ++i) { + cpuTimes[i] = Long.parseLong(uidTimesStr[i + 1]); + } + return cpuTimes; + } + + private void resetBatteryStats() throws Exception { + executeCmd("dumpsys batterystats --reset"); + } + + private void batteryOnScreenOn() throws Exception { + batteryOn(); + screenOn(); + } + + private void batteryOnScreenOff() throws Exception { + batteryOn(); + screenoff(); + } + + private void batteryOffScreenOn() throws Exception { + batteryOff(); + screenOn(); + } + + private void batteryOn() throws Exception { + executeCmd("dumpsys battery unplug"); + assertBatteryState(false); + } + + private void batteryOff() throws Exception { + executeCmd("dumpsys battery reset"); + assertBatteryState(true); + } + + private void screenOn() throws Exception { + executeCmd("input keyevent KEYCODE_WAKEUP"); + executeCmd("wm dismiss-keyguard"); + assertKeyguardUnLocked(); + assertScreenInteractive(true); + } + + private void screenoff() throws Exception { + executeCmd("input keyevent KEYCODE_SLEEP"); + assertScreenInteractive(false); + } + + private void forceStop() throws Exception { + executeCmd("cmd activity force-stop " + TEST_PKG); + assertUidState(PROCESS_STATE_NONEXISTENT); + } + + private void assertUidState(int state) throws Exception { + final String uidStateStr = executeCmd("cmd activity get-uid-state " + sTestPkgUid); + final int uidState = Integer.parseInt(uidStateStr.split(" ")[0]); + assertEquals(state, uidState); + } + + private void assertKeyguardUnLocked() { + final KeyguardManager keyguardManager = + (KeyguardManager) sContext.getSystemService(Context.KEYGUARD_SERVICE); + assertDelayedCondition("Keyguard should be unlocked", + () -> !keyguardManager.isKeyguardLocked()); + } + + private void assertScreenInteractive(boolean interactive) { + final PowerManager powerManager = + (PowerManager) sContext.getSystemService(Context.POWER_SERVICE); + assertDelayedCondition("Unexpected screen interactive state", + () -> interactive == powerManager.isInteractive()); + } + + private void assertDelayedCondition(String errorMsg, ExpectedCondition condition) { + final long endTime = SystemClock.uptimeMillis() + GENERAL_TIMEOUT_MS; + while (SystemClock.uptimeMillis() <= endTime) { + if (condition.isTrue()) { + return; + } + SystemClock.sleep(GENERAL_INTERVAL_MS); + } + if (!condition.isTrue()) { + fail(errorMsg); + } + } + + private void assertBatteryState(boolean pluggedIn) throws Exception { + final long endTime = SystemClock.uptimeMillis() + BATTERY_STATE_TIMEOUT_MS; + while (isDevicePluggedIn() != pluggedIn && SystemClock.uptimeMillis() <= endTime) { + Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS); + } + if (isDevicePluggedIn() != pluggedIn) { + fail("Timed out waiting for the plugged-in state to change," + + " expected pluggedIn: " + pluggedIn); + } + } + + private boolean isDevicePluggedIn() { + final Intent batteryIntent = sContext.registerReceiver(null, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0; + } + + private String executeCmd(String cmd) throws Exception { + final String result = sUiDevice.executeShellCommand(cmd).trim(); + Log.d(TAG, String.format("Result for '%s': %s", cmd, result)); + return result; + } + + private static String executeCmdSilent(String cmd) throws Exception { + return sUiDevice.executeShellCommand(cmd).trim(); + } + + private interface ExpectedCondition { + boolean isTrue(); + } +} diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 158df136534a..da0205d72125 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -47,12 +47,12 @@ ApkAssets::ApkAssets(void* unmanaged_handle, const std::string& path) } std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) { - return ApkAssets::LoadImpl(path, nullptr, nullptr, system, false /*load_as_shared_library*/); + return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, false /*load_as_shared_library*/); } std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path, bool system) { - return ApkAssets::LoadImpl(path, nullptr, nullptr, system, true /*load_as_shared_library*/); + return LoadImpl({} /*fd*/, path, nullptr, nullptr, system, true /*load_as_shared_library*/); } std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path, @@ -70,8 +70,15 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap LOG(ERROR) << "failed to load IDMAP " << idmap_path; return {}; } - return LoadImpl(loaded_idmap->OverlayApkPath(), std::move(idmap_asset), std::move(loaded_idmap), - system, false /*load_as_shared_library*/); + return LoadImpl({} /*fd*/, loaded_idmap->OverlayApkPath(), std::move(idmap_asset), + std::move(loaded_idmap), system, false /*load_as_shared_library*/); +} + +std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd, + const std::string& friendly_name, + bool system, bool force_shared_lib) { + return LoadImpl(std::move(fd), friendly_name, nullptr /*idmap_asset*/, nullptr /*loaded_idmap*/, + system, force_shared_lib); } std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) { @@ -96,11 +103,19 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) { } std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( - const std::string& path, std::unique_ptr<Asset> idmap_asset, + unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library) { ATRACE_CALL(); + ::ZipArchiveHandle unmanaged_handle; - int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); + int32_t result; + if (fd >= 0) { + result = + ::OpenArchiveFd(fd.release(), path.c_str(), &unmanaged_handle, true /*assume_ownership*/); + } else { + result = ::OpenArchive(path.c_str(), &unmanaged_handle); + } + if (result != 0) { LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); return {}; diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 3a307fc9ea0a..69702e314442 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -21,6 +21,7 @@ #include <string> #include "android-base/macros.h" +#include "android-base/unique_fd.h" #include "androidfw/Asset.h" #include "androidfw/LoadedArsc.h" @@ -51,6 +52,16 @@ class ApkAssets { static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path, bool system = false); + // Creates an ApkAssets from the given file descriptor, and takes ownership of the file + // descriptor. The `friendly_name` is some name that will be used to identify the source of + // this ApkAssets in log messages and other debug scenarios. + // If `system` is true, the package is marked as a system package, and allows some functions to + // filter out this package when computing what configurations/resources are available. + // If `force_shared_lib` is true, any package with ID 0x7f is loaded as a shared library. + static std::unique_ptr<const ApkAssets> LoadFromFd(base::unique_fd fd, + const std::string& friendly_name, bool system, + bool force_shared_lib); + std::unique_ptr<Asset> Open(const std::string& path, Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const; @@ -69,7 +80,7 @@ class ApkAssets { private: DISALLOW_COPY_AND_ASSIGN(ApkAssets); - static std::unique_ptr<const ApkAssets> LoadImpl(const std::string& path, + static std::unique_ptr<const ApkAssets> LoadImpl(base::unique_fd fd, const std::string& path, std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> loaded_idmap, bool system, bool load_as_shared_library); diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index d65d93f9af1a..ba5844bbfbb1 100644 --- a/libs/androidfw/tests/ApkAssets_test.cpp +++ b/libs/androidfw/tests/ApkAssets_test.cpp @@ -24,6 +24,7 @@ #include "TestHelpers.h" #include "data/basic/R.h" +using ::android::base::unique_fd; using ::com::android::basic::R; namespace android { @@ -44,6 +45,26 @@ TEST(ApkAssetsTest, LoadApk) { ASSERT_NE(nullptr, asset); } +TEST(ApkAssetsTest, LoadApkFromFd) { + const std::string path = GetTestDataPath() + "/basic/basic.apk"; + unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY)); + ASSERT_GE(fd.get(), 0); + + std::unique_ptr<const ApkAssets> loaded_apk = + ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/); + ASSERT_NE(nullptr, loaded_apk); + + const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc(); + ASSERT_NE(nullptr, loaded_arsc); + + const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000); + ASSERT_NE(nullptr, loaded_package); + EXPECT_TRUE(loaded_package->IsVerified()); + + std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml"); + ASSERT_NE(nullptr, asset); +} + TEST(ApkAssetsTest, LoadApkAsSharedLibrary) { std::unique_ptr<const ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk"); @@ -132,7 +153,7 @@ TEST(ApkAssetsTest, OpenUncompressedAssetFd) { ASSERT_NE(nullptr, asset); off64_t start, length; - base::unique_fd fd(asset->openFileDescriptor(&start, &length)); + unique_fd fd(asset->openFileDescriptor(&start, &length)); EXPECT_GE(fd.get(), 0); lseek64(fd.get(), start, SEEK_SET); diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 6c87a9d8d915..143182f83ace 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -1586,8 +1586,10 @@ public final class TvInputManager { * @param info The TV input which will use the acquired Hardware. * @return Hardware on success, {@code null} otherwise. * + * @hide * @removed */ + @SystemApi @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public Hardware acquireTvInputHardware(int deviceId, final HardwareCallback callback, TvInputInfo info) { diff --git a/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml b/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml deleted file mode 100644 index a0b8155c0818..000000000000 --- a/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2017 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. - --> - - -<!-- Based off frameworks/base/core/res/res/layout/preference_dropdown_material.xml - except that icon space in this layout is always reserved --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <Spinner - android:id="@+id/spinner" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/preference_no_icon_padding_start" - android:visibility="invisible" /> - - <include layout="@layout/preference_material"/> - -</FrameLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml index cf9f3c67137c..d7032b8c4b26 100644 --- a/packages/SettingsLib/res/values/styles_support_preference.xml +++ b/packages/SettingsLib/res/values/styles_support_preference.xml @@ -20,73 +20,14 @@ <dimen name="preference_no_icon_padding_start">72dp</dimen> - <!-- Fragment style --> - <style name="PreferenceFragmentStyle.SettingsBase" parent="@*android:style/PreferenceFragment.Material"> - <item name="allowDividerAfterLastItem">false</item> - </style> - - <!-- Preferences --> - <style name="Preference.SettingsBase" parent="@style/Preference.Material"> - <item name="allowDividerAbove">false</item> - <item name="allowDividerBelow">true</item> - <item name="singleLineTitle">false</item> - <item name="iconSpaceReserved">true</item> - </style> - - <!-- Preference category --> - <style name="Preference.Category.SettingsBase" parent="@style/Preference.Category.Material"> - <item name="allowDividerAbove">true</item> - <item name="allowDividerBelow">true</item> - <item name="android:layout">@layout/preference_category_material_settings</item> - </style> - - <!-- Preference screen --> - <style name="Preference.Screen.SettingsBase" parent="@style/Preference.PreferenceScreen.Material"> - <item name="allowDividerAbove">false</item> - <item name="allowDividerBelow">true</item> - <item name="iconSpaceReserved">true</item> - </style> - <!-- Footer Preferences --> - <style name="Preference.FooterPreference.SettingsBase" parent="Preference.SettingsBase"> + <style name="Preference.FooterPreference.SettingsBase" parent="@style/Preference.Material"> <item name="android:layout">@layout/preference_footer</item> <item name="allowDividerAbove">true</item> </style> - <!-- Dropdown Preferences --> - <style name="Preference.DropdownPreference.SettingsBase" parent="Preference.SettingsBase"> - <item name="android:layout">@layout/preference_dropdown_material_settings</item> - </style> - - <!-- Switch Preferences --> - <style name="Preference.SwitchPreference.SettingsBase" parent="@style/Preference.SwitchPreference.Material"> - <item name="allowDividerAbove">false</item> - <item name="allowDividerBelow">true</item> - <item name="iconSpaceReserved">true</item> - <item name="singleLineTitle">false</item> - </style> - - <!-- EditText Preferences --> - <style name="Preference.EditTextPreference.SettingsBase" - parent="@style/Preference.DialogPreference.EditTextPreference.Material"> - <item name="allowDividerAbove">false</item> - <item name="allowDividerBelow">true</item> - <item name="iconSpaceReserved">true</item> - <item name="singleLineTitle">false</item> - </style> - <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay.v14.Material"> - <!-- Parent path frameworks/support/v14/preference/res/values/themes.xml --> - <item name="android:scrollbars">vertical</item> - <item name="preferenceFragmentStyle">@style/PreferenceFragmentStyle.SettingsBase</item> - <item name="preferenceCategoryStyle">@style/Preference.Category.SettingsBase</item> - <item name="preferenceScreenStyle">@style/Preference.Screen.SettingsBase</item> - <item name="preferenceStyle">@style/Preference.SettingsBase</item> - <item name="dialogPreferenceStyle">@style/Preference.SettingsBase</item> - <item name="editTextPreferenceStyle">@style/Preference.EditTextPreference.SettingsBase</item> <item name="footerPreferenceStyle">@style/Preference.FooterPreference.SettingsBase</item> - <item name="switchPreferenceStyle">@style/Preference.SwitchPreference.SettingsBase</item> - <item name="dropdownPreferenceStyle">@style/Preference.DropdownPreference.SettingsBase</item> </style> </resources>
\ No newline at end of file diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index ba1c9e3cc43b..52b4f4df5735 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -801,6 +801,9 @@ class SettingsProtoDumpUtil { Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX, GlobalSettingsProto.BLUETOOTH_PAN_PRIORITY_PREFIX); dumpSetting(s, p, + Settings.Global.BLUETOOTH_HEARING_AID_PRIORITY_PREFIX, + GlobalSettingsProto.BLUETOOTH_HEARING_AID_PRIORITY_PREFIX); + dumpSetting(s, p, Settings.Global.ACTIVITY_MANAGER_CONSTANTS, GlobalSettingsProto.ACTIVITY_MANAGER_CONSTANTS); dumpSetting(s, p, diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b132160d4504..ca85445e197e 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -67,7 +67,7 @@ <string name="battery_low_percent_format"><xliff:g id="percentage">%s</xliff:g> remaining</string> <!-- Same as battery_low_percent_format, with a notice about battery saver if on. [CHAR LIMIT=none]--> - <string name="battery_low_percent_format_saver_started"><xliff:g id="percentage">%s</xliff:g> remaining. Battery saver is on.</string> + <string name="battery_low_percent_format_saver_started"><xliff:g id="percentage">%s</xliff:g> remaining. Battery Saver is on.</string> <!-- A message that appears when a USB charger is plugged in and the device does not support charging on it. That is, a charger that fits into the USB port and goes into @@ -86,13 +86,13 @@ <string name="battery_low_why">Settings</string> <!-- Battery saver confirmation dialog title [CHAR LIMIT=NONE]--> - <string name="battery_saver_confirmation_title">Turn on battery saver?</string> + <string name="battery_saver_confirmation_title">Turn on Battery Saver?</string> <!-- Battery saver confirmation dialog ok text [CHAR LIMIT=40]--> <string name="battery_saver_confirmation_ok">Turn on</string> <!-- Battery saver notification action [CHAR LIMIT=NONE]--> - <string name="battery_saver_start_action">Turn on battery saver</string> + <string name="battery_saver_start_action">Turn on Battery Saver</string> <!-- Name of the button that links to the Settings app. [CHAR LIMIT=NONE] --> <string name="status_bar_settings_settings_button">Settings</string> @@ -583,18 +583,6 @@ <!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] --> <string name="data_usage_disabled_dialog_enable">Resume</string> - <!-- Text to display underneath the graphical signal strength meter when - no connection is available. [CHAR LIMIT=20] --> - <string name="status_bar_settings_signal_meter_disconnected"> - No Internet connection - </string> - - <!-- Text to display underneath the graphical signal strength meter when - it is displaying Wi-Fi status and Wi-Fi is connected to a network - whose SSID is not available. - [CHAR LIMIT=20] --> - <string name="status_bar_settings_signal_meter_wifi_nossid">Wi-Fi connected</string> - <!-- Notification text: when GPS is getting a fix [CHAR LIMIT=50] --> <string name="gps_notification_searching_text">Searching for GPS</string> @@ -989,13 +977,13 @@ <string name="user_remove_user_remove">Remove</string> <!-- Battery saver notification title. [CHAR LIMIT=60]--> - <string name="battery_saver_notification_title">Battery saver is on</string> + <string name="battery_saver_notification_title">Battery Saver is on</string> <!-- Battery saver notification text. [CHAR LIMIT=60] --> <string name="battery_saver_notification_text">Reduces performance and background data</string> <!-- Battery saver notification action text. [CHAR LIMIT=60] --> - <string name="battery_saver_notification_action_text">Turn off battery saver</string> + <string name="battery_saver_notification_action_text">Turn off Battery Saver</string> <!-- Media projection permission dialog warning text. [CHAR LIMIT=NONE] --> <string name="media_projection_dialog_text"><xliff:g id="app_seeking_permission" example="Hangouts">%s</xliff:g> will start capturing everything that\'s displayed on your screen.</string> @@ -1532,10 +1520,10 @@ <string name="battery_panel_title">Battery usage</string> <!-- Summary of battery saver not available [CHAR LIMIT=NONE] --> - <string name="battery_detail_charging_summary">Battery saver not available during charging</string> + <string name="battery_detail_charging_summary">Battery Saver not available during charging</string> <!-- Title of switch for battery saver [CHAR LIMIT=NONE] --> - <string name="battery_detail_switch_title">Battery saver</string> + <string name="battery_detail_switch_title">Battery Saver</string> <!-- Summary of switch for battery saver [CHAR LIMIT=NONE] --> <string name="battery_detail_switch_summary">Reduces performance and background data</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index f6fab86cefaa..eb2d12edd059 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -319,32 +319,39 @@ public class ActivityManagerWrapper { mBackgroundExecutor.submit(new Runnable() { @Override public void run() { + boolean result = false; try { - ActivityManager.getService().startActivityFromRecents(taskKey.id, - finalOptions == null ? null : finalOptions.toBundle()); - if (resultCallback != null) { - resultCallbackHandler.post(new Runnable() { - @Override - public void run() { - resultCallback.accept(true); - } - }); - } + result = startActivityFromRecents(taskKey.id, finalOptions); } catch (Exception e) { - if (resultCallback != null) { - resultCallbackHandler.post(new Runnable() { - @Override - public void run() { - resultCallback.accept(false); - } - }); - } + // Fall through + } + final boolean finalResult = result; + if (resultCallback != null) { + resultCallbackHandler.post(new Runnable() { + @Override + public void run() { + resultCallback.accept(finalResult); + } + }); } } }); } /** + * Starts a task from Recents synchronously. + */ + public boolean startActivityFromRecents(int taskId, ActivityOptions options) { + try { + Bundle optsBundle = options == null ? null : options.toBundle(); + ActivityManager.getService().startActivityFromRecents(taskId, optsBundle); + return true; + } catch (Exception e) { + return false; + } + } + + /** * Registers a task stack listener with the system. * This should be called on the main thread. */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java new file mode 100644 index 000000000000..705a21522b0a --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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.shared.system; + +import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; +import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; + +import android.app.ActivityOptions; + +/** + * Wrapper around internal ActivityOptions creation. + */ +public abstract class ActivityOptionsCompat { + + /** + * @return ActivityOptions for starting a task in split screen. + */ + public static ActivityOptions makeSplitScreenOptions(boolean dockTopLeft) { + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + options.setSplitScreenCreateMode(dockTopLeft + ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT + : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT); + return options; + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 1477558a0e97..225dbb4aafbe 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -19,8 +19,15 @@ package com.android.systemui.shared.system; import static android.view.Display.DEFAULT_DISPLAY; import android.graphics.Rect; +import android.os.Handler; +import android.os.IRemoteCallback; +import android.os.RemoteException; +import android.util.Log; import android.view.WindowManagerGlobal; +import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; +import com.android.systemui.shared.recents.view.RecentsTransition; + public class WindowManagerWrapper { private static final String TAG = "WindowManagerWrapper"; @@ -38,8 +45,24 @@ public class WindowManagerWrapper { try { WindowManagerGlobal.getWindowManagerService().getStableInsets(DEFAULT_DISPLAY, outStableInsets); - } catch (Exception e) { - e.printStackTrace(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get stable insets", e); + } + } + + /** + * Overrides a pending app transition. + */ + public void overridePendingAppTransitionMultiThumbFuture( + AppTransitionAnimationSpecsFuture animationSpecFuture, + Runnable animStartedCallback, Handler animStartedCallbackHandler, boolean scaleUp) { + try { + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionMultiThumbFuture(animationSpecFuture.getFuture(), + RecentsTransition.wrapStartedListener(animStartedCallbackHandler, + animStartedCallback), scaleUp); + } catch (RemoteException e) { + Log.w(TAG, "Failed to override pending app transition (multi-thumbnail future): ", e); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 3b1b2f9079d5..9400fd8a4db5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -156,7 +156,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // Launched from app is always the worst case (in terms of how many // thumbnails/tasks visible) launchState.launchedFromApp = true; - mBackgroundLayoutAlgorithm.update(plan.getTaskStack(), EMPTY_SET, launchState); + mBackgroundLayoutAlgorithm.update(plan.getTaskStack(), EMPTY_SET, launchState, + -1 /* lastScrollPPresent */); VisibilityReport visibilityReport = mBackgroundLayoutAlgorithm.computeStackVisibilityReport( stack.getTasks()); diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index d89bab758bd0..cf3cae5a7c04 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -21,12 +21,10 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.StackInfo; import android.app.ActivityOptions; @@ -49,9 +47,6 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.IRemoteCallback; -import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -63,7 +58,6 @@ import android.service.dreams.IDreamManager; import android.util.Log; import android.util.MutableBoolean; import android.view.Display; -import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDockedStackListener; import android.view.IWindowManager; import android.view.WindowManager; @@ -78,12 +72,9 @@ import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImpl; -import com.android.systemui.shared.recents.model.Task; -import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.statusbar.policy.UserInfoController; import java.util.List; -import java.util.function.Consumer; /** * Acts as a shim around the real system services that we need to access data from, and provides @@ -268,22 +259,6 @@ public class SystemServicesProxy { return mIsSafeMode; } - /** Docks a task to the side of the screen and starts it. */ - public boolean startTaskInDockedMode(int taskId, int createMode) { - if (mIam == null) return false; - - try { - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setSplitScreenCreateMode(createMode); - options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - mIam.startActivityFromRecents(taskId, options.toBundle()); - return true; - } catch (Exception e) { - Log.e(TAG, "Failed to dock task: " + taskId + " with createMode: " + createMode, e); - } - return false; - } - /** Moves an already resumed task to the side of the screen to initiate split screen. */ public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, Rect initialBounds) { @@ -540,16 +515,6 @@ public class SystemServicesProxy { } } - public void overridePendingAppTransitionMultiThumbFuture( - IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener, - boolean scaleUp) { - try { - mIwm.overridePendingAppTransitionMultiThumbFuture(future, animStartedListener, scaleUp); - } catch (RemoteException e) { - Log.w(TAG, "Failed to override transition: " + e); - } - } - /** * Updates the visibility of recents. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 1440fc168cc6..e3ed1aaa5506 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -16,13 +16,14 @@ package com.android.systemui.recents.views; +import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; + import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.Nullable; import android.app.ActivityOptions; -import android.app.ActivityOptions.OnAnimationStartedListener; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Canvas; @@ -33,11 +34,11 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Handler; +import android.os.IRemoteCallback; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; -import android.view.AppTransitionAnimationSpec; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -86,13 +87,15 @@ import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; import com.android.systemui.recents.misc.ReferenceCountedTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; -import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.TaskStack; +import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat; import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture; import com.android.systemui.shared.recents.view.RecentsTransition; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.ActivityOptionsCompat; +import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.stackdivider.WindowManagerProxy; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.phone.ScrimController; @@ -608,16 +611,17 @@ public class RecentsView extends FrameLayout { // rect to its final layout-space rect Utilities.setViewFrameFromTranslation(event.taskView); - // Dock the task and launch it - SystemServicesProxy ssp = Recents.getSystemServices(); - if (ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode)) { + final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions( + dockState.createMode == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT); + if (ActivityManagerWrapper.getInstance().startActivityFromRecents(event.task.key.id, + options)) { final Runnable animStartedListener = () -> { EventBus.getDefault().send(new DockedFirstAnimationFrameEvent()); - // Remove the task and don't bother relaying out, as all the tasks will be - // relaid out when the stack changes on the multiwindow change event + // Remove the task and don't bother relaying out, as all the tasks + // will be relaid out when the stack changes on the multiwindow + // change event getStack().removeTask(event.task, null, true /* fromDockGesture */); }; - final Rect taskRect = getTaskRect(event.taskView); AppTransitionAnimationSpecsFuture future = new AppTransitionAnimationSpecsFuture( getHandler()) { @@ -626,10 +630,8 @@ public class RecentsView extends FrameLayout { return mTransitionHelper.composeDockAnimationSpec(event.taskView, taskRect); } }; - ssp.overridePendingAppTransitionMultiThumbFuture(future.getFuture(), - RecentsTransition.wrapStartedListener(getHandler(), animStartedListener), - true /* scaleUp */); - + WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture( + future, animStartedListener, getHandler(), true /* scaleUp */); MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP, event.task.getTopComponent().flattenToShortString()); } else { @@ -1032,11 +1034,9 @@ public class RecentsView extends FrameLayout { if (taskIndex > -1) { taskIndexFromFront = stack.getTaskCount() - taskIndex - 1; } - EventBus.getDefault().send(new LaunchTaskSucceededEvent( - taskIndexFromFront)); + EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront)); } else { - Log.e(TAG, mContext.getString(R.string.recents_launch_error_message, - task.title)); + Log.e(TAG, mContext.getString(R.string.recents_launch_error_message, task.title)); // Dismiss the task if we fail to launch it if (taskView != null) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 600da0410fc4..d9f79bb6d34e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -431,7 +431,7 @@ public class TaskStackLayoutAlgorithm { * in the stack. */ public void update(TaskStack stack, ArraySet<Task.TaskKey> ignoreTasksSet, - RecentsActivityLaunchState launchState) { + RecentsActivityLaunchState launchState, float lastScrollPPercent) { SystemServicesProxy ssp = Recents.getSystemServices(); // Clear the progress map @@ -506,6 +506,8 @@ public class TaskStackLayoutAlgorithm { if (launchState.launchedWithAltTab) { mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP); + } else if (0 <= lastScrollPPercent && lastScrollPPercent <= 1) { + mInitialScrollP = Utilities.mapRange(lastScrollPPercent, mMinScrollP, mMaxScrollP); } else if (Recents.getConfiguration().isLowRamDevice) { mInitialScrollP = mTaskStackLowRamLayoutAlgorithm.getInitialScrollP(mNumStackTasks, scrollToFront); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 1197501268a6..36c9095fe8ae 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -209,6 +209,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal private int mLastHeight; private boolean mStackActionButtonVisible; + // Percentage of last ScrollP from the min to max scrollP that lives after configuration changes + private float mLastScrollPPercent; + // We keep track of the task view focused by user interaction and draw a frame around it in the // grid layout. private TaskViewFocusFrame mTaskViewFocusFrame; @@ -327,6 +330,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mStackScroller.reset(); mStableLayoutAlgorithm.reset(); mLayoutAlgorithm.reset(); + mLastScrollPPercent = -1; } // Since we always animate to the same place in (the initial state), always reset the stack @@ -822,7 +826,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal public void updateLayoutAlgorithm(boolean boundScrollToNewMinMax, RecentsActivityLaunchState launchState) { // Compute the min and max scroll values - mLayoutAlgorithm.update(mStack, mIgnoreTasks, launchState); + mLayoutAlgorithm.update(mStack, mIgnoreTasks, launchState, mLastScrollPPercent); if (boundScrollToNewMinMax) { mStackScroller.boundScroll(); @@ -1150,6 +1154,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal if (mTaskViewsClipDirty) { clipTaskViews(); } + mLastScrollPPercent = Utilities.clamp(Utilities.unmapRange(mStackScroller.getStackScroll(), + mLayoutAlgorithm.mMinScrollP, mLayoutAlgorithm.mMaxScrollP), 0, 1); } /** diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java index 1cda30111db8..da798848f7e8 100644 --- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java @@ -20,6 +20,8 @@ import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIG import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.os.UserHandle.USER_CURRENT; +import static com.android.systemui.statusbar.phone.NavigationBarGestureHelper.DRAG_MODE_NONE; + import android.app.ActivityManager; import android.content.res.Configuration; import android.os.RemoteException; @@ -36,6 +38,7 @@ import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.DividerView; +import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; import java.util.List; @@ -89,20 +92,11 @@ public class ShortcutKeyDispatcher extends SystemUI try { int dockSide = mWindowManagerService.getDockedStackSide(); if (dockSide == WindowManager.DOCKED_INVALID) { - // If there is no window docked, we dock the top-most window. + // Split the screen Recents recents = getComponent(Recents.class); - int dockMode = (shortcutCode == SC_DOCK_LEFT) + recents.splitPrimaryTask(DRAG_MODE_NONE, (shortcutCode == SC_DOCK_LEFT) ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT - : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; - List<ActivityManager.RecentTaskInfo> taskList = - ActivityManagerWrapper.getInstance().getRecentTasks(1, USER_CURRENT); - recents.showRecentApps( - false /* triggeredFromAltTab */, - false /* fromHome */); - if (!taskList.isEmpty()) { - SystemServicesProxy.getInstance(mContext).startTaskInDockedMode( - taskList.get(0).id, dockMode); - } + : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1); } else { // If there is already a docked window, we respond by resizing the docking pane. DividerView dividerView = getComponent(Divider.class).getView(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java index eb211a10737b..4d58cb88dd13 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationMediaTemplateViewWrapper.java @@ -58,6 +58,6 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi @Override public boolean isDimmable() { - return false; + return getCustomBackgroundColor() == 0; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java index c9500363e9d8..b81a3b0416a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java @@ -80,7 +80,8 @@ public final class NavigationBarTransitions extends BarTransitions { @Override protected boolean isLightsOut(int mode) { - return super.isLightsOut(mode) || (mAutoDim && !mWallpaperVisible); + return super.isLightsOut(mode) || (mAutoDim && !mWallpaperVisible + && mode != MODE_WARNING); } public LightBarTransitionsController getLightTransitionsController() { @@ -108,7 +109,9 @@ public final class NavigationBarTransitions extends BarTransitions { // ok, everyone, stop it right there navButtons.animate().cancel(); - final float navButtonsAlpha = lightsOut ? 0.6f : 1f; + // Bump percentage by 10% if dark. + float darkBump = mLightTransitionsController.getCurrentDarkIntensity() / 10; + final float navButtonsAlpha = lightsOut ? 0.6f + darkBump : 1f; if (!animate) { navButtons.setAlpha(navButtonsAlpha); @@ -130,6 +133,9 @@ public final class NavigationBarTransitions extends BarTransitions { for (int i = buttonDispatchers.size() - 1; i >= 0; i--) { buttonDispatchers.valueAt(i).setDarkIntensity(darkIntensity); } + if (mAutoDim) { + applyLightsOut(false, true); + } } private final View.OnTouchListener mLightsOutListener = new View.OnTouchListener() { diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 98efd522970b..32af29d24aa2 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -173,6 +173,7 @@ message MetricsEvent { // Selection invocation methods. Used as sub-type for TEXT_SELECTION_SESSION events. enum TextSelectionInvocationMethod { + TEXT_SELECTION_INVOCATION_UNKNOWN = 0; TEXT_SELECTION_INVOCATION_MANUAL = 1; TEXT_SELECTION_INVOCATION_LINK = 2; } @@ -2805,316 +2806,524 @@ message MetricsEvent { // OS: O TEXT_LONGPRESS = 629; - // OBSOLETE + // ACTION: An app requested an unknown permission + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_UNKNOWN = 630; - // OBSOLETE + // ACTION: An app was granted an unknown permission + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_UNKNOWN = 631; - // OBSOLETE + // ACTION: An app requested an unknown permission and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_UNKNOWN = 632; - // OBSOLETE + // ACTION: An unknown permission was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_UNKNOWN = 633; - // OBSOLETE + // ACTION: An app requested the permission READ_CALENDAR + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_READ_CALENDAR = 634; - // OBSOLETE + // ACTION: An app was granted the permission READ_CALENDAR + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_READ_CALENDAR = 635; - // OBSOLETE + // ACTION: An app requested the permission READ_CALENDAR and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_READ_CALENDAR = 636; - // OBSOLETE + // ACTION: The permission READ_CALENDAR was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_READ_CALENDAR = 637; - // OBSOLETE + // ACTION: An app requested the permission WRITE_CALENDAR + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_WRITE_CALENDAR = 638; - // OBSOLETE + // ACTION: An app was granted the permission WRITE_CALENDAR + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_WRITE_CALENDAR = 639; - // OBSOLETE + // ACTION: An app requested the permission WRITE_CALENDAR and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_WRITE_CALENDAR = 640; - // OBSOLETE + // ACTION: The permission WRITE_CALENDAR was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_WRITE_CALENDAR = 641; - // OBSOLETE + // ACTION: An app requested the permission CAMERA + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_CAMERA = 642; - // OBSOLETE + // ACTION: An app was granted the permission CAMERA + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_CAMERA = 643; - // OBSOLETE + // ACTION: An app requested the permission CAMERA and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_CAMERA = 644; - // OBSOLETE + // ACTION: The permission CAMERA was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_CAMERA = 645; - // AOBSOLETE + // ACTION: An app requested the permission READ_CONTACTS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_READ_CONTACTS = 646; - // OBSOLETE + // ACTION: An app was granted the permission READ_CONTACTS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_READ_CONTACTS = 647; - // OBSOLETE + // ACTION: An app requested the permission READ_CONTACTS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_READ_CONTACTS = 648; - // OBSOLETE + // ACTION: The permission READ_CONTACTS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_READ_CONTACTS = 649; - // OBSOLETE + // ACTION: An app requested the permission WRITE_CONTACTS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_WRITE_CONTACTS = 650; - // OBSOLETE + // ACTION: An app was granted the permission WRITE_CONTACTS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_WRITE_CONTACTS = 651; - // OBSOLETE + // ACTION: An app requested the permission WRITE_CONTACTS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_WRITE_CONTACTS = 652; - // OBSOLETE + // ACTION: The permission WRITE_CONTACTS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_WRITE_CONTACTS = 653; - // OBSOLETE + // ACTION: An app requested the permission GET_ACCOUNTS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_GET_ACCOUNTS = 654; - // OBSOLETE + // ACTION: An app was granted the permission GET_ACCOUNTS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_GET_ACCOUNTS = 655; - // OBSOLETE + // ACTION: An app requested the permission GET_ACCOUNTS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_GET_ACCOUNTS = 656; - // OBSOLETE + // ACTION: The permission GET_ACCOUNTS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_GET_ACCOUNTS = 657; - // AOBSOLETE + // ACTION: An app requested the permission ACCESS_FINE_LOCATION + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_ACCESS_FINE_LOCATION = 658; - // OBSOLETE + // ACTION: An app was granted the permission ACCESS_FINE_LOCATION + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_ACCESS_FINE_LOCATION = 659; - // OBSOLETE + // ACTION: An app requested the permission ACCESS_FINE_LOCATION and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_ACCESS_FINE_LOCATION = 660; - // OBSOLETE + // ACTION: The permission ACCESS_FINE_LOCATION was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_ACCESS_FINE_LOCATION = 661; - // OBSOLETE + // ACTION: An app requested the permission ACCESS_COARSE_LOCATION + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_ACCESS_COARSE_LOCATION = 662; - // OBSOLETE + // ACTION: An app was granted the permission ACCESS_COARSE_LOCATION + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_ACCESS_COARSE_LOCATION = 663; - // OBSOLETE + // ACTION: An app requested the permission ACCESS_COARSE_LOCATION and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_ACCESS_COARSE_LOCATION = 664; - // OBSOLETE + // ACTION: The permission ACCESS_COARSE_LOCATION was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_ACCESS_COARSE_LOCATION = 665; - // OBSOLETE + // ACTION: An app requested the permission RECORD_AUDIO + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_RECORD_AUDIO = 666; - // OBSOLETE + // ACTION: An app was granted the permission RECORD_AUDIO + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_RECORD_AUDIO = 667; - // OBSOLETE + // ACTION: An app requested the permission RECORD_AUDIO and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_RECORD_AUDIO = 668; - // OBSOLETE + // ACTION: The permission RECORD_AUDIO was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_RECORD_AUDIO = 669; - // OBSOLETE + // ACTION: An app requested the permission READ_PHONE_STATE + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_READ_PHONE_STATE = 670; - // OBSOLETE + // ACTION: An app was granted the permission READ_PHONE_STATE + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_READ_PHONE_STATE = 671; - // OBSOLETE + // ACTION: An app requested the permission READ_PHONE_STATE and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_READ_PHONE_STATE = 672; - // OBSOLETE + // ACTION: The permission READ_PHONE_STATE was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_READ_PHONE_STATE = 673; - // OBSOLETE + // ACTION: An app requested the permission CALL_PHONE + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_CALL_PHONE = 674; - // OBSOLETE + // ACTION: An app was granted the permission CALL_PHONE + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_CALL_PHONE = 675; - // OBSOLETE + // ACTION: An app requested the permission CALL_PHONE and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_CALL_PHONE = 676; - // OBSOLETE + // ACTION: The permission CALL_PHONE was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_CALL_PHONE = 677; - // AOBSOLETE + // ACTION: An app requested the permission READ_CALL_LOG + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_READ_CALL_LOG = 678; - // OBSOLETE + // ACTION: An app was granted the permission READ_CALL_LOG + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_READ_CALL_LOG = 679; - // OBSOLETE + // ACTION: An app requested the permission READ_CALL_LOG and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_READ_CALL_LOG = 680; - // OBSOLETE + // ACTION: The permission READ_CALL_LOG was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_READ_CALL_LOG = 681; - // OBSOLETE + // ACTION: An app requested the permission WRITE_CALL_LOG + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_WRITE_CALL_LOG = 682; - // OBSOLETE + // ACTION: An app was granted the permission WRITE_CALL_LOG + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_WRITE_CALL_LOG = 683; - // OBSOLETE + // ACTION: An app requested the permission WRITE_CALL_LOG and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_WRITE_CALL_LOG = 684; - // OBSOLETE + // ACTION: The permission WRITE_CALL_LOG was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_WRITE_CALL_LOG = 685; - // OBSOLETE + // ACTION: An app requested the permission ADD_VOICEMAIL + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_ADD_VOICEMAIL = 686; - // OBSOLETE + // ACTION: An app was granted the permission ADD_VOICEMAIL + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_ADD_VOICEMAIL = 687; - // OBSOLETE + // ACTION: An app requested the permission ADD_VOICEMAIL and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_ADD_VOICEMAIL = 688; - // OBSOLETE + // ACTION: The permission ADD_VOICEMAIL was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_ADD_VOICEMAIL = 689; - // OBSOLETE + // ACTION: An app requested the permission USE_SIP + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_USE_SIP = 690; - // OBSOLETE + // ACTION: An app was granted the permission USE_SIP + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_USE_SIP = 691; - // OBSOLETE + // ACTION: An app requested the permission USE_SIP and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_USE_SIP = 692; - // OBSOLETE + // ACTION: The permission USE_SIP was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_USE_SIP = 693; - // OBSOLETE + // ACTION: An app requested the permission PROCESS_OUTGOING_CALLS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_PROCESS_OUTGOING_CALLS = 694; - // OBSOLETE + // ACTION: An app was granted the permission PROCESS_OUTGOING_CALLS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_PROCESS_OUTGOING_CALLS = 695; - // OBSOLETE + // ACTION: An app requested the permission PROCESS_OUTGOING_CALLS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_PROCESS_OUTGOING_CALLS = 696; - // OBSOLETE + // ACTION: The permission PROCESS_OUTGOING_CALLS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_PROCESS_OUTGOING_CALLS = 697; - // OBSOLETE + // ACTION: An app requested the permission READ_CELL_BROADCASTS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_READ_CELL_BROADCASTS = 698; - // OBSOLETE + // ACTION: An app was granted the permission READ_CELL_BROADCASTS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_READ_CELL_BROADCASTS = 699; - // OBSOLETE + // ACTION: An app requested the permission READ_CELL_BROADCASTS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_READ_CELL_BROADCASTS = 700; - // OBSOLETE + // ACTION: The permission READ_CELL_BROADCASTS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_READ_CELL_BROADCASTS = 701; - // OBSOLETE + // ACTION: An app requested the permission BODY_SENSORS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_BODY_SENSORS = 702; - // OBSOLETE + // ACTION: An app was granted the permission BODY_SENSORS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_BODY_SENSORS = 703; - // OBSOLETE + // ACTION: An app requested the permission BODY_SENSORS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_BODY_SENSORS = 704; - // OBSOLETE + // ACTION: The permission BODY_SENSORS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_BODY_SENSORS = 705; - // OBSOLETE + // ACTION: An app requested the permission SEND_SMS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_SEND_SMS = 706; - // OBSOLETE + // ACTION: An app was granted the permission SEND_SMS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_SEND_SMS = 707; - // OBSOLETE + // ACTION: An app requested the permission SEND_SMS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_SEND_SMS = 708; - // OBSOLETE + // ACTION: The permission SEND_SMS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_SEND_SMS = 709; - // OBSOLETE + // ACTION: An app requested the permission RECEIVE_SMS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_RECEIVE_SMS = 710; - // OBSOLETE + // ACTION: An app was granted the permission RECEIVE_SMS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_RECEIVE_SMS = 711; - // OBSOLETE + // ACTION: An app requested the permission RECEIVE_SMS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_RECEIVE_SMS = 712; - // OBSOLETE + // ACTION: The permission RECEIVE_SMS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_RECEIVE_SMS = 713; - // OBSOLETE + // ACTION: An app requested the permission READ_SMS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_READ_SMS = 714; - // OBSOLETE + // ACTION: An app was granted the permission READ_SMS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_READ_SMS = 715; - // OBSOLETE + // ACTION: An app requested the permission READ_SMS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_READ_SMS = 716; - // OBSOLETE + // ACTION: The permission READ_SMS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_READ_SMS = 717; - // OBSOLETE + // ACTION: An app requested the permission RECEIVE_WAP_PUSH + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_RECEIVE_WAP_PUSH = 718; - // OBSOLETE + // ACTION: An app was granted the permission RECEIVE_WAP_PUSH + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_RECEIVE_WAP_PUSH = 719; - // OBSOLETE + // ACTION: An app requested the permission RECEIVE_WAP_PUSH and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_RECEIVE_WAP_PUSH = 720; - // OBSOLETE + // ACTION: The permission RECEIVE_WAP_PUSH was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_RECEIVE_WAP_PUSH = 721; - // OBSOLETE + // ACTION: An app requested the permission RECEIVE_MMS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_RECEIVE_MMS = 722; - // OBSOLETE + // ACTION: An app was granted the permission RECEIVE_MMS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_RECEIVE_MMS = 723; - // OBSOLETE + // ACTION: An app requested the permission RECEIVE_MMS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_RECEIVE_MMS = 724; - // OBSOLETE + // ACTION: The permission RECEIVE_MMS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_RECEIVE_MMS = 725; - // OBSOLETE + // ACTION: An app requested the permission READ_EXTERNAL_STORAGE + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 726; - // OBSOLETE + // ACTION: An app was granted the permission READ_EXTERNAL_STORAGE + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_READ_EXTERNAL_STORAGE = 727; - // OBSOLETE + // ACTION: An app requested the permission READ_EXTERNAL_STORAGE and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_READ_EXTERNAL_STORAGE = 728; - // OBSOLETE + // ACTION: The permission READ_EXTERNAL_STORAGE was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_READ_EXTERNAL_STORAGE = 729; - // OBSOLETE + // ACTION: An app requested the permission WRITE_EXTERNAL_STORAGE + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 730; - // OBSOLETE + // ACTION: An app was granted the permission WRITE_EXTERNAL_STORAGE + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_WRITE_EXTERNAL_STORAGE = 731; - // OBSOLETE + // ACTION: An app requested the permission WRITE_EXTERNAL_STORAGE and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_WRITE_EXTERNAL_STORAGE = 732; - // OBSOLETE + // ACTION: The permission WRITE_EXTERNAL_STORAGE was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_WRITE_EXTERNAL_STORAGE = 733; // ACTION: Logged when a provisioning session has started @@ -3123,16 +3332,24 @@ message MetricsEvent { // ACTION: Logged when a provisioning session has completed PROVISIONING_SESSION_COMPLETED = 735; - // OBSOLETE + // ACTION: An app requested the permission READ_PHONE_NUMBERS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_REQUEST_READ_PHONE_NUMBERS = 736; - // OBSOLETE + // ACTION: An app was granted the permission READ_PHONE_NUMBERS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_PERMISSION_GRANT_READ_PHONE_NUMBERS = 737; - // OBSOLETE + // ACTION: An app requested the permission READ_PHONE_NUMBERS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_PERMISSION_DENIED_READ_PHONE_NUMBERS = 738; - // OBSOLETE + // ACTION: The permission READ_PHONE_NUMBERS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_PERMISSION_REVOKE_READ_PHONE_NUMBERS = 739; // ACTION: QS Brightness Slider (with auto brightness disabled, and VR enabled) @@ -3624,52 +3841,84 @@ message MetricsEvent { // CATEGORY: SETTINGS SETTINGS_LOCK_SCREEN_PREFERENCES = 882; - // OBSOLETE + // ACTION: An app requested the app-op permission ACCESS_NOTIFICATIONS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_APPOP_REQUEST_ACCESS_NOTIFICATIONS = 883; - // OBSOLETE + // ACTION: An app was granted the app-op permission ACCESS_NOTIFICATIONS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_APPOP_GRANT_ACCESS_NOTIFICATIONS = 884; - // OBSOLETE + // ACTION: An app requested the app-op permission ACCESS_NOTIFICATIONS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_APPOP_DENIED_ACCESS_NOTIFICATIONS = 885; - // OBSOLETE + // ACTION: The app-op permission ACCESS_NOTIFICATIONS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_APPOP_REVOKE_ACCESS_NOTIFICATIONS = 886; - // OBSOLETE + // ACTION: An app requested the app-op permission SYSTEM_ALERT_WINDOW + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_APPOP_REQUEST_SYSTEM_ALERT_WINDOW = 887; - // OBSOLETE + // ACTION: An app was granted the app-op permission SYSTEM_ALERT_WINDOW + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_APPOP_GRANT_SYSTEM_ALERT_WINDOW = 888; - // OBSOLETE + // ACTION: An app requested the app-op permission SYSTEM_ALERT_WINDOW and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_APPOP_DENIED_SYSTEM_ALERT_WINDOW = 889; - // OBSOLETE + // ACTION: The app-op permission SYSTEM_ALERT_WINDOW was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_APPOP_REVOKE_SYSTEM_ALERT_WINDOW = 890; - // OBSOLETE + // ACTION: An app requested the app-op permission REQUEST_WRITE_SETTINGS + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_APPOP_REQUEST_WRITE_SETTINGS = 891; - // OBSOLETE + // ACTION: An app was granted the app-op permission REQUEST_WRITE_SETTINGS + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_APPOP_GRANT_WRITE_SETTINGS = 892; - // OBSOLETE + // ACTION: An app requested the app-op permission REQUEST_WRITE_SETTINGS and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_APPOP_DENIED_WRITE_SETTINGS = 893; - // OBSOLETE + // ACTION: The app-op permission REQUEST_WRITE_SETTINGS was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_APPOP_REVOKE_WRITE_SETTINGS = 894; - // OBSOLETE + // ACTION: An app requested the app-op permission REQUEST_INSTALL_PACKAGES + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_APPOP_REQUEST_REQUEST_INSTALL_PACKAGES = 895; - // OBSOLETE + // ACTION: An app was granted the app-op permission REQUEST_INSTALL_PACKAGES + // PACKAGE: The package name of the app that was granted the permission + // OBSOLETE as of Android P ACTION_APPOP_GRANT_REQUEST_INSTALL_PACKAGES = 896; - // OBSOLETE + // ACTION: An app requested the app-op permission REQUEST_INSTALL_PACKAGES and the request was denied + // PACKAGE: The package name of the app requesting the permission + // OBSOLETE as of Android P ACTION_APPOP_DENIED_REQUEST_INSTALL_PACKAGES = 897; - // OBSOLETE + // ACTION: The app-op permission REQUEST_INSTALL_PACKAGES was revoked for an app + // PACKAGE: The package name of the app the permission was revoked for + // OBSOLETE as of Android P ACTION_APPOP_REVOKE_REQUEST_INSTALL_PACKAGES = 898; // ACTION: Phase 1 of instant application resolution occurred @@ -3761,7 +4010,8 @@ message MetricsEvent { FIELD_AUTOFILL_NUM_IDS = 917; // ACTION: An autofill service was reqiested to save data - // Type TYPE_SUCCESS: The request succeeded + // Type TYPE_SUCCESS: The request succeeded right away + // Type TYPE_OPEN: The request succeeded but the service launched an IntentSender // Type TYPE_FAILURE: The request failed // Package: Package of app that was autofilled // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request @@ -4763,6 +5013,8 @@ message MetricsEvent { // OS: P FIELD_SELECTION_WIDGET_VERSION = 1262; + // ---- End P Constants, all P constants go above this line ---- + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java index af55807ff1f0..831c488e95b2 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java @@ -26,6 +26,7 @@ import android.annotation.Nullable; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentSender; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; @@ -100,7 +101,8 @@ final class RemoteFillService implements DeathRecipient { @NonNull String servicePackageName); void onFillRequestFailure(@Nullable CharSequence message, @NonNull String servicePackageName); - void onSaveRequestSuccess(@NonNull String servicePackageName); + void onSaveRequestSuccess(@NonNull String servicePackageName, + @Nullable IntentSender intentSender); void onSaveRequestFailure(@Nullable CharSequence message, @NonNull String servicePackageName); void onServiceDied(RemoteFillService service); @@ -308,10 +310,11 @@ final class RemoteFillService implements DeathRecipient { }); } - private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest) { + private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest, + IntentSender intentSender) { mHandler.getHandler().post(() -> { if (handleResponseCallbackCommon(pendingRequest)) { - mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName()); + mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), intentSender); } }); } @@ -624,12 +627,13 @@ final class RemoteFillService implements DeathRecipient { mCallback = new ISaveCallback.Stub() { @Override - public void onSuccess() { + public void onSuccess(IntentSender intentSender) { if (!finish()) return; final RemoteFillService remoteService = getService(); if (remoteService != null) { - remoteService.dispatchOnSaveRequestSuccess(PendingSaveRequest.this); + remoteService.dispatchOnSaveRequestSuccess(PendingSaveRequest.this, + intentSender); } } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 974148623b00..99b92b9c9cd4 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -561,7 +561,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // FillServiceCallbacks @Override - public void onSaveRequestSuccess(@NonNull String servicePackageName) { + public void onSaveRequestSuccess(@NonNull String servicePackageName, + @Nullable IntentSender intentSender) { synchronized (mLock) { mIsSaving = false; @@ -572,8 +573,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST, servicePackageName) - .setType(MetricsEvent.TYPE_SUCCESS); + .setType(intentSender == null ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_OPEN); mMetricsLogger.write(log); + if (intentSender != null) { + if (sDebug) Slog.d(TAG, "Starting intent sender on save()"); + startIntentSender(intentSender); + } // Nothing left to do... removeSelf(); diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index 2289f8572b93..3dbee42f2e79 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -42,6 +42,8 @@ import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.WindowConfiguration; +import android.graphics.Point; +import android.graphics.Rect; import android.util.IntArray; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -89,6 +91,9 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { private ActivityStack mPinnedStack = null; private ActivityStack mSplitScreenPrimaryStack = null; + // Used in updating the display size + private Point mTmpDisplaySize = new Point(); + ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) { mSupervisor = supervisor; mDisplayId = displayId; @@ -97,6 +102,13 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> { throw new IllegalStateException("Display does not exist displayId=" + displayId); } mDisplay = display; + + updateBounds(); + } + + void updateBounds() { + mDisplay.getSize(mTmpDisplaySize); + setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y); } void addChild(ActivityStack stack, int position) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ae91b8208b72..3c8f112cfab0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -713,6 +713,12 @@ public class ActivityManagerService extends IActivityManager.Stub final UserController mUserController; + /** + * Packages that are being allowed to perform unrestricted app switches. Mapping is + * User -> Type -> uid. + */ + final SparseArray<ArrayMap<String, Integer>> mAllowAppSwitchUids = new SparseArray<>(); + final AppErrors mAppErrors; final AppWarnings mAppWarnings; @@ -2494,7 +2500,7 @@ public class ActivityManagerService extends IActivityManager.Stub DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO); ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats); ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false, - DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); + DUMP_FLAG_PRIORITY_HIGH); ServiceManager.addService("gfxinfo", new GraphicsBinder(this)); ServiceManager.addService("dbinfo", new DbBinder(this)); if (MONITOR_CPU_USAGE) { @@ -2552,9 +2558,15 @@ public class ActivityManagerService extends IActivityManager.Stub private final PriorityDump.PriorityDumper mPriorityDumper = new PriorityDump.PriorityDumper() { @Override - public void dumpNormal(FileDescriptor fd, PrintWriter pw, String[] args, + public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { if (asProto) return; + mActivityManagerService.dumpApplicationMemoryUsage(fd, + pw, " ", new String[] {"-a"}, false, null); + } + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) { + if (asProto) return; mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null); } }; @@ -2866,6 +2878,7 @@ public class ActivityManagerService extends IActivityManager.Stub void onUserStoppedLocked(int userId) { mRecentTasks.unloadUserDataFromMemoryLocked(userId); + mAllowAppSwitchUids.remove(userId); } public void initPowerManagement() { @@ -8092,7 +8105,7 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } // An activity is consider to be in multi-window mode if its task isn't fullscreen. - return !r.getTask().mFullscreen; + return r.inMultiWindowMode(); } } finally { Binder.restoreCallingIdentity(origId); @@ -10111,8 +10124,8 @@ public class ActivityManagerService extends IActivityManager.Stub } else { // Task isn't in window manager yet since it isn't associated with a stack. // Return the persist value from activity manager - if (task.mBounds != null) { - rect.set(task.mBounds); + if (!task.matchParentBounds()) { + rect.set(task.getBounds()); } else if (task.mLastNonFullscreenBounds != null) { rect.set(task.mLastNonFullscreenBounds); } @@ -12605,6 +12618,18 @@ public class ActivityManagerService extends IActivityManager.Stub } } + boolean checkAllowAppSwitchUid(int uid) { + ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(UserHandle.getUserId(uid)); + if (types != null) { + for (int i = types.size() - 1; i >= 0; i--) { + if (types.valueAt(i).intValue() == uid) { + return true; + } + } + } + return false; + } + boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid, int callingPid, int callingUid, String name) { if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) { @@ -12617,6 +12642,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (perm == PackageManager.PERMISSION_GRANTED) { return true; } + if (checkAllowAppSwitchUid(sourceUid)) { + return true; + } // If the actual IPC caller is different from the logical source, then // also see if they are allowed to control app switches. @@ -12627,6 +12655,9 @@ public class ActivityManagerService extends IActivityManager.Stub if (perm == PackageManager.PERMISSION_GRANTED) { return true; } + if (checkAllowAppSwitchUid(callingUid)) { + return true; + } } Slog.w(TAG, name + " request from " + sourceUid + " stopped"); @@ -14903,6 +14934,7 @@ public class ActivityManagerService extends IActivityManager.Stub boolean dumpVisibleStacksOnly = false; boolean dumpFocusedStackOnly = false; String dumpPackage = null; + int dumpAppId = -1; int opti = 0; while (opti < args.length) { @@ -14997,6 +15029,16 @@ public class ActivityManagerService extends IActivityManager.Stub return; } + if (dumpPackage != null) { + try { + ApplicationInfo info = mContext.getPackageManager().getApplicationInfo( + dumpPackage, 0); + dumpAppId = UserHandle.getAppId(info.uid); + } catch (NameNotFoundException e) { + e.printStackTrace(); + } + } + boolean more = false; // Is the caller requesting to dump a particular piece of data? if (opti < args.length) { @@ -15104,7 +15146,7 @@ public class ActivityManagerService extends IActivityManager.Stub args.length - opti); } synchronized (this) { - dumpProcessesLocked(fd, pw, args, opti, true, dumpPackage); + dumpProcessesLocked(fd, pw, args, opti, true, dumpPackage, dumpAppId); } } else if ("oom".equals(cmd) || "o".equals(cmd)) { synchronized (this) { @@ -15290,7 +15332,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage); + dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId); } } else { @@ -15367,7 +15409,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage); + dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage, dumpAppId); } } Binder.restoreCallingIdentity(origId); @@ -15522,22 +15564,12 @@ public class ActivityManagerService extends IActivityManager.Stub } } - boolean dumpUids(PrintWriter pw, String dumpPackage, SparseArray<UidRecord> uids, + boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, SparseArray<UidRecord> uids, String header, boolean needSep) { boolean printed = false; - int whichAppId = -1; - if (dumpPackage != null) { - try { - ApplicationInfo info = mContext.getPackageManager().getApplicationInfo( - dumpPackage, 0); - whichAppId = UserHandle.getAppId(info.uid); - } catch (NameNotFoundException e) { - e.printStackTrace(); - } - } for (int i=0; i<uids.size(); i++) { UidRecord uidRec = uids.valueAt(i); - if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) { + if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) { continue; } if (!printed) { @@ -15584,7 +15616,7 @@ public class ActivityManagerService extends IActivityManager.Stub } void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, String dumpPackage) { + int opti, boolean dumpAll, String dumpPackage, int dumpAppId) { boolean needSep = false; boolean printedAnything = false; int numPers = 0; @@ -15662,13 +15694,14 @@ public class ActivityManagerService extends IActivityManager.Stub } if (mActiveUids.size() > 0) { - if (dumpUids(pw, dumpPackage, mActiveUids, "UID states:", needSep)) { + if (dumpUids(pw, dumpPackage, dumpAppId, mActiveUids, "UID states:", needSep)) { printedAnything = needSep = true; } } if (dumpAll) { if (mValidateUids.size() > 0) { - if (dumpUids(pw, dumpPackage, mValidateUids, "UID validation:", needSep)) { + if (dumpUids(pw, dumpPackage, dumpAppId, mValidateUids, "UID validation:", + needSep)) { printedAnything = needSep = true; } } @@ -15972,6 +16005,32 @@ public class ActivityManagerService extends IActivityManager.Stub pw.println(" mNativeDebuggingApp=" + mNativeDebuggingApp); } } + if (mAllowAppSwitchUids.size() > 0) { + boolean printed = false; + for (int i = 0; i < mAllowAppSwitchUids.size(); i++) { + ArrayMap<String, Integer> types = mAllowAppSwitchUids.valueAt(i); + for (int j = 0; j < types.size(); j++) { + if (dumpPackage == null || + UserHandle.getAppId(types.valueAt(j).intValue()) == dumpAppId) { + if (needSep) { + pw.println(); + needSep = false; + } + if (!printed) { + pw.println(" mAllowAppSwitchUids:"); + printed = true; + } + pw.print(" User "); + pw.print(mAllowAppSwitchUids.keyAt(i)); + pw.print(": Type "); + pw.print(types.keyAt(j)); + pw.print(" = "); + UserHandle.formatUid(pw, types.valueAt(j).intValue()); + pw.println(); + } + } + } + } if (dumpPackage == null) { if (mAlwaysFinishActivities) { pw.println(" mAlwaysFinishActivities=" + mAlwaysFinishActivities); @@ -17950,7 +18009,7 @@ public class ActivityManagerService extends IActivityManager.Stub PrintWriter catPw = new FastPrintWriter(catSw, false, 256); String[] emptyArgs = new String[] { }; catPw.println(); - dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); + dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null, -1); catPw.println(); mServices.newServiceDumperLocked(null, catPw, emptyArgs, 0, false, null).dumpLocked(); @@ -24250,6 +24309,27 @@ public class ActivityManagerService extends IActivityManager.Stub } } } + + @Override + public void setAllowAppSwitches(@NonNull String type, int uid, int userId) { + synchronized (ActivityManagerService.this) { + if (mUserController.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) { + ArrayMap<String, Integer> types = mAllowAppSwitchUids.get(userId); + if (types == null) { + if (uid < 0) { + return; + } + types = new ArrayMap<>(); + mAllowAppSwitchUids.put(userId, types); + } + if (uid < 0) { + types.remove(type); + } else { + types.put(type, uid); + } + } + } + } } /** diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index a2c46f14ed3a..11590d6ca83b 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -165,7 +165,6 @@ import android.view.IApplicationToken; import android.view.WindowManager.LayoutParams; import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; import com.android.internal.util.XmlUtils; @@ -343,12 +342,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // on the window. int mRotationAnimationHint = -1; - // The bounds of this activity. Mainly used for aspect-ratio compatibility. - // TODO(b/36505427): Every level on ConfigurationContainer now has bounds information, which - // directly affects the configuration. We should probably move this into that class and have it - // handle calculating override configuration from the bounds. - private final Rect mBounds = new Rect(); - private boolean mShowWhenLocked; private boolean mTurnScreenOn; @@ -414,8 +407,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (!getOverrideConfiguration().equals(EMPTY)) { pw.println(prefix + "OverrideConfiguration=" + getOverrideConfiguration()); } - if (!mBounds.isEmpty()) { - pw.println(prefix + "mBounds=" + mBounds); + if (!matchParentBounds()) { + pw.println(prefix + "bounds=" + getBounds()); } if (resultTo != null || resultWho != null) { pw.print(prefix); pw.print("resultTo="); pw.print(resultTo); @@ -648,7 +641,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } // An activity is considered to be in multi-window mode if its task isn't fullscreen. - final boolean inMultiWindowMode = !task.mFullscreen; + final boolean inMultiWindowMode = task.inMultiWindowMode(); if (inMultiWindowMode != mLastReportedMultiWindowMode) { mLastReportedMultiWindowMode = inMultiWindowMode; scheduleMultiWindowModeChanged(getConfiguration()); @@ -966,14 +959,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges, task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(), appInfo.targetSdkVersion, mRotationAnimationHint, - ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L, mBounds); + ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L); task.addActivityToTop(this); // When an activity is started directly into a split-screen fullscreen stack, we need to // update the initial multi-window modes so that the callbacks are scheduled correctly when // the user leaves that mode. - mLastReportedMultiWindowMode = !task.mFullscreen; + mLastReportedMultiWindowMode = inMultiWindowMode(); mLastReportedPictureInPictureMode = inPinnedWindowingMode(); } @@ -2172,33 +2165,25 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo mLastReportedConfiguration.setConfiguration(global, override); } - @Override - public void onOverrideConfigurationChanged(Configuration newConfig) { - final Configuration currentConfig = getOverrideConfiguration(); - if (currentConfig.equals(newConfig)) { - return; - } - super.onOverrideConfigurationChanged(newConfig); - if (mWindowContainerController == null) { - return; - } - mWindowContainerController.onOverrideConfigurationChanged(newConfig, mBounds); - } - // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private void updateOverrideConfiguration() { mTmpConfig.unset(); computeBounds(mTmpBounds); - if (mTmpBounds.equals(mBounds)) { + + if (mTmpBounds.equals(getOverrideBounds())) { return; } - mBounds.set(mTmpBounds); + setBounds(mTmpBounds); + + final Rect updatedBounds = getOverrideBounds(); + // Bounds changed...update configuration to match. - if (!mBounds.isEmpty()) { - task.computeOverrideConfiguration(mTmpConfig, mBounds, null /* insetBounds */, + if (!matchParentBounds()) { + task.computeOverrideConfiguration(mTmpConfig, updatedBounds, null /* insetBounds */, false /* overrideWidth */, false /* overrideHeight */); } + onOverrideConfigurationChanged(mTmpConfig); } @@ -2225,7 +2210,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo outBounds.setEmpty(); final float maxAspectRatio = info.maxAspectRatio; final ActivityStack stack = getStack(); - if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0 + if (task == null || stack == null || task.inMultiWindowMode() || maxAspectRatio == 0 || isInVrUiMode(getConfiguration())) { // We don't set override configuration if that activity task isn't fullscreen. I.e. the // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for @@ -2256,11 +2241,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo if (containingAppWidth <= maxActivityWidth && containingAppHeight <= maxActivityHeight) { // The display matches or is less than the activity aspect ratio, so nothing else to do. // Return the existing bounds. If this method is running for the first time, - // {@link mBounds} will be empty (representing no override). If the method has run - // before, then effect of {@link mBounds} will already have been applied to the + // {@link #getOverrideBounds()} will be empty (representing no override). If the method has run + // before, then effect of {@link #getOverrideBounds()} will already have been applied to the // value returned from {@link getConfiguration}. Refer to // {@link TaskRecord#computeOverrideConfiguration}. - outBounds.set(mBounds); + outBounds.set(getOverrideBounds()); return; } @@ -2272,12 +2257,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo outBounds.offsetTo(left, 0 /* top */); } - /** Get bounds of the activity. */ - @VisibleForTesting - Rect getBounds() { - return new Rect(mBounds); - } - /** * Make sure the given activity matches the current configuration. Returns false if the activity * had to be destroyed. Returns true if the configuration is the same, or the activity will diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 6985d6e36ac1..481699833e41 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -156,7 +156,6 @@ import java.util.Set; */ class ActivityStack<T extends StackWindowController> extends ConfigurationContainer implements StackWindowListener { - private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM; private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE; private static final String TAG_APP = TAG + POSTFIX_APP; @@ -322,11 +321,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai */ boolean mForceHidden = false; - // Whether or not this stack covers the entire screen; by default stacks are fullscreen - boolean mFullscreen = true; - // Current bounds of the stack or null if fullscreen. - Rect mBounds = null; - private boolean mUpdateBoundsDeferred; private boolean mUpdateBoundsDeferredCalled; private final Rect mDeferredBounds = new Rect(); @@ -342,8 +336,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai /** The attached Display's unique identifier, or -1 if detached */ int mDisplayId; - /** Temp variables used during override configuration update. */ - private final SparseArray<Configuration> mTmpConfigs = new SparseArray<>(); private final SparseArray<Rect> mTmpBounds = new SparseArray<>(); private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>(); private final Rect mTmpRect2 = new Rect(); @@ -574,7 +566,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - if (!Objects.equals(mBounds, mTmpRect2)) { + if (!Objects.equals(getOverrideBounds(), mTmpRect2)) { resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */); } } finally { @@ -641,9 +633,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai */ private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) { mDisplayId = activityDisplay.mDisplayId; - mBounds = bounds != null ? new Rect(bounds) : null; - mFullscreen = mBounds == null; - + setBounds(bounds); onParentChanged(); activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM); @@ -651,7 +641,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // If we created a docked stack we want to resize it so it resizes all other stacks // in the system. mStackSupervisor.resizeDockedStackLocked( - mBounds, null, null, null, null, PRESERVE_WINDOWS); + getOverrideBounds(), null, null, null, null, PRESERVE_WINDOWS); } } @@ -766,8 +756,9 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai return false; } - void setBounds(Rect bounds) { - mBounds = mFullscreen ? null : new Rect(bounds); + @Override + public int setBounds(Rect bounds) { + return super.setBounds(!inMultiWindowMode() ? null : bounds); } ActivityRecord topRunningActivityLocked() { @@ -2495,7 +2486,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // apps, maybeUpdateTransitToWallpaper() will fail to identify this as a // TRANSIT_WALLPAPER_OPEN animation, and run some funny animation. final boolean lastActivityTranslucent = lastStack != null - && (!lastStack.mFullscreen + && (lastStack.inMultiWindowMode() || (lastStack.mLastPausedActivity != null && !lastStack.mLastPausedActivity.fullscreen)); @@ -2739,8 +2730,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai position = getAdjustedPositionForTask(task, position, null /* starting */); mTaskHistory.remove(task); mTaskHistory.add(position, task); - mWindowContainerController.positionChildAt(task.getWindowContainerController(), position, - task.mBounds, task.getOverrideConfiguration()); + mWindowContainerController.positionChildAt(task.getWindowContainerController(), position); updateTaskMovement(task, true); } @@ -4602,8 +4592,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // TODO: Can only be called from special methods in ActivityStackSupervisor. // Need to consolidate those calls points into this resize method so anyone can call directly. void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) { - bounds = TaskRecord.validateBounds(bounds); - if (!updateBoundsAllowed(bounds, tempTaskBounds, tempTaskInsetBounds)) { return; } @@ -4613,7 +4601,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final Rect insetBounds = tempTaskInsetBounds != null ? tempTaskInsetBounds : taskBounds; mTmpBounds.clear(); - mTmpConfigs.clear(); mTmpInsetBounds.clear(); for (int i = mTaskHistory.size() - 1; i >= 0; i--) { @@ -4624,7 +4611,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai // For freeform stack we don't adjust the size of the tasks to match that // of the stack, but we do try to make sure the tasks are still contained // with the bounds of the stack. - mTmpRect2.set(task.mBounds); + mTmpRect2.set(task.getOverrideBounds()); fitWithinBounds(mTmpRect2, bounds); task.updateOverrideConfiguration(mTmpRect2); } else { @@ -4632,15 +4619,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai } } - mTmpConfigs.put(task.taskId, task.getOverrideConfiguration()); - mTmpBounds.put(task.taskId, task.mBounds); + mTmpBounds.put(task.taskId, task.getOverrideBounds()); if (tempTaskInsetBounds != null) { mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds); } } - mFullscreen = mWindowContainerController.resize(bounds, mTmpConfigs, mTmpBounds, - mTmpInsetBounds); + mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds); setBounds(bounds); } @@ -4655,7 +4640,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai * @param stackBounds Bounds within which the other bounds should remain. */ private static void fitWithinBounds(Rect bounds, Rect stackBounds) { - if (stackBounds == null || stackBounds.contains(bounds)) { + if (stackBounds == null || stackBounds.isEmpty() || stackBounds.contains(bounds)) { return; } @@ -4873,8 +4858,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai pw.println(""); } pw.println(prefix + "Task id #" + task.taskId); - pw.println(prefix + "mFullscreen=" + task.mFullscreen); - pw.println(prefix + "mBounds=" + task.mBounds); + pw.println(prefix + "mBounds=" + task.getOverrideBounds()); pw.println(prefix + "mMinWidth=" + task.mMinWidth); pw.println(prefix + "mMinHeight=" + task.mMinHeight); pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds); @@ -4981,7 +4965,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP && mStackSupervisor.isFocusedStack(this)) { String myReason = reason + " leftTaskHistoryEmpty"; - if (mFullscreen || !adjustFocusToNextFocusableStack(myReason)) { + if (!inMultiWindowMode() || !adjustFocusToNextFocusableStack(myReason)) { mStackSupervisor.moveHomeStackToFront(myReason); } } @@ -5011,8 +4995,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController() .isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY); if (!mStackSupervisor.getLaunchingBoundsController().layoutTask(task, info.windowLayout) - && mBounds != null && task.isResizeable() && !isLockscreenShown) { - task.updateOverrideConfiguration(mBounds); + && !matchParentBounds() && task.isResizeable() && !isLockscreenShown) { + task.updateOverrideConfiguration(getOverrideBounds()); } task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0); return task; @@ -5174,10 +5158,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai mResumedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY); } proto.write(DISPLAY_ID, mDisplayId); - if (mBounds != null) { - mBounds.writeToProto(proto, BOUNDS); + if (!matchParentBounds()) { + final Rect bounds = getOverrideBounds(); + bounds.writeToProto(proto, BOUNDS); } - proto.write(FULLSCREEN, mFullscreen); + + // TODO: Remove, no longer needed with windowingMode. + proto.write(FULLSCREEN, matchParentBounds()); proto.end(token); } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index ddde4bc4957c..5525cdb380b7 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2118,7 +2118,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) { - final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds()); + final Rect bounds = options.getLaunchBounds(); task.updateOverrideConfiguration(bounds); ActivityStack stack = getLaunchStack(null, options, task, ON_TOP); @@ -2626,7 +2626,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // TODO: Checking for isAttached might not be needed as if the user passes in null // dockedBounds then they want the docked stack to be dismissed. - if (stack.mFullscreen || (dockedBounds == null && !stack.isAttached())) { + if (stack.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + || (dockedBounds == null && !stack.isAttached())) { // The dock stack either was dismissed or went fullscreen, which is kinda the same. // In this case we make all other static stacks fullscreen and move all // docked stack tasks to the fullscreen stack. @@ -3058,7 +3059,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // Resize the pinned stack to match the current size of the task the activity we are // going to be moving is currently contained in. We do this to have the right starting // animation bounds for the pinned stack to the desired bounds the caller wants. - resizeStackLocked(stack, task.mBounds, null /* tempTaskBounds */, + resizeStackLocked(stack, task.getOverrideBounds(), null /* tempTaskBounds */, null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, true /* allowResizeInDockedMode */, !DEFER_RESUME); @@ -3782,9 +3783,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D pw.println(" Stack #" + stack.mStackId + ": type=" + activityTypeToString(stack.getActivityType()) + " mode=" + windowingModeToString(stack.getWindowingMode())); - pw.println(" mFullscreen=" + stack.mFullscreen); pw.println(" isSleeping=" + stack.shouldSleepActivities()); - pw.println(" mBounds=" + stack.mBounds); + pw.println(" mBounds=" + stack.getOverrideBounds()); printed |= stack.dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage, needSep); @@ -4054,6 +4054,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private void handleDisplayChanged(int displayId) { synchronized (mService) { ActivityDisplay activityDisplay = mActivityDisplays.get(displayId); + // TODO: The following code block should be moved into {@link ActivityDisplay}. if (activityDisplay != null) { // The window policy is responsible for stopping activities on the default display if (displayId != Display.DEFAULT_DISPLAY) { @@ -4067,7 +4068,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D activityDisplay.mOffToken = null; } } - // TODO: Update the bounds. + + activityDisplay.updateBounds(); } mWindowManager.onDisplayChanged(displayId); } @@ -4289,7 +4291,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return; } - scheduleUpdatePictureInPictureModeIfNeeded(task, stack.mBounds); + scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getOverrideBounds()); } void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) { diff --git a/services/core/java/com/android/server/am/LaunchingActivityPositioner.java b/services/core/java/com/android/server/am/LaunchingActivityPositioner.java index d5f9cf3ad4d5..793884d08c08 100644 --- a/services/core/java/com/android/server/am/LaunchingActivityPositioner.java +++ b/services/core/java/com/android/server/am/LaunchingActivityPositioner.java @@ -47,10 +47,10 @@ public class LaunchingActivityPositioner implements LaunchingBoundsPositioner { return RESULT_SKIP; } - final Rect bounds = TaskRecord.validateBounds(options.getLaunchBounds()); + final Rect bounds = options.getLaunchBounds(); // Bounds weren't valid. - if (bounds == null) { + if (bounds == null || bounds.isEmpty()) { return RESULT_SKIP; } diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java index c958fcac6938..d89568e27506 100644 --- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java +++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java @@ -88,7 +88,7 @@ class LaunchingTaskPositioner implements LaunchingBoundsController.LaunchingBoun final ArrayList<TaskRecord> tasks = task.getStack().getAllTasks(); - updateAvailableRect(task, mAvailableRect); + mAvailableRect.set(task.getParent().getBounds()); if (layout == null) { positionCenter(tasks, mAvailableRect, getFreeformWidth(mAvailableRect), @@ -123,17 +123,6 @@ class LaunchingTaskPositioner implements LaunchingBoundsController.LaunchingBoun return RESULT_CONTINUE; } - private void updateAvailableRect(TaskRecord task, Rect availableRect) { - final Rect stackBounds = task.getStack().mBounds; - - if (stackBounds != null) { - availableRect.set(stackBounds); - } else { - task.getStack().getDisplay().mDisplay.getSize(mDisplaySize); - availableRect.set(0, 0, mDisplaySize.x, mDisplaySize.y); - } - } - @VisibleForTesting static int getFreeformStartLeft(Rect bounds) { return bounds.left + bounds.width() / MARGIN_SIZE_DENOMINATOR; @@ -294,9 +283,9 @@ class LaunchingTaskPositioner implements LaunchingBoundsController.LaunchingBoun private static boolean boundsConflict(Rect proposal, ArrayList<TaskRecord> tasks) { for (int i = tasks.size() - 1; i >= 0; i--) { - TaskRecord task = tasks.get(i); - if (!task.mActivities.isEmpty() && task.mBounds != null) { - Rect bounds = task.mBounds; + final TaskRecord task = tasks.get(i); + if (!task.mActivities.isEmpty() && !task.matchParentBounds()) { + final Rect bounds = task.getOverrideBounds(); if (closeLeftTopCorner(proposal, bounds) || closeRightTopCorner(proposal, bounds) || closeLeftBottomCorner(proposal, bounds) || closeRightBottomCorner(proposal, bounds)) { diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index d35c37b54431..6e6d7e9f2945 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -1426,9 +1426,6 @@ class RecentTasks { * Creates a new RecentTaskInfo from a TaskRecord. */ ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) { - // Update the task description to reflect any changes in the task stack - tr.updateTaskDescription(); - // Compose the recent task info ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId; @@ -1444,8 +1441,8 @@ class RecentTasks { rti.affiliatedTaskId = tr.mAffiliatedTaskId; rti.affiliatedTaskColor = tr.mAffiliatedTaskColor; rti.numActivities = 0; - if (tr.mBounds != null) { - rti.bounds = new Rect(tr.mBounds); + if (!tr.matchParentBounds()) { + rti.bounds = new Rect(tr.getOverrideBounds()); } rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode(); rti.resizeMode = tr.mResizeMode; diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index a9c6eee05bf6..365990f15835 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -19,9 +19,6 @@ package com.android.server.am; import static android.app.ActivityManager.RESIZE_MODE_FORCED; import static android.app.ActivityManager.RESIZE_MODE_SYSTEM; import static android.app.ActivityManager.StackId.INVALID_STACK_ID; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; @@ -293,11 +290,6 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi final ActivityManagerService mService; - // Whether or not this task covers the entire screen; by default tasks are fullscreen. - boolean mFullscreen = true; - - // Bounds of the Task. null for fullscreen tasks. - Rect mBounds = null; private final Rect mTmpStableBounds = new Rect(); private final Rect mTmpNonDecorBounds = new Rect(); private final Rect mTmpRect = new Rect(); @@ -480,68 +472,76 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } boolean resize(Rect bounds, int resizeMode, boolean preserveWindow, boolean deferResume) { - if (!isResizeable()) { - Slog.w(TAG, "resizeTask: task " + this + " not resizeable."); - return true; - } + mService.mWindowManager.deferSurfaceLayout(); - // If this is a forced resize, let it go through even if the bounds is not changing, - // as we might need a relayout due to surface size change (to/from fullscreen). - final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0; - if (Objects.equals(mBounds, bounds) && !forced) { - // Nothing to do here... - return true; - } - bounds = validateBounds(bounds); - - if (mWindowContainerController == null) { - // Task doesn't exist in window manager yet (e.g. was restored from recents). - // All we can do for now is update the bounds so it can be used when the task is - // added to window manager. - updateOverrideConfiguration(bounds); - if (!inFreeformWindowingMode()) { - // re-restore the task so it can have the proper stack association. - mService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP); + try { + if (!isResizeable()) { + Slog.w(TAG, "resizeTask: task " + this + " not resizeable."); + return true; } - return true; - } - if (!canResizeToBounds(bounds)) { - throw new IllegalArgumentException("resizeTask: Can not resize task=" + this - + " to bounds=" + bounds + " resizeMode=" + mResizeMode); - } + // If this is a forced resize, let it go through even if the bounds is not changing, + // as we might need a relayout due to surface size change (to/from fullscreen). + final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0; + if (equivalentOverrideBounds(bounds) && !forced) { + // Nothing to do here... + return true; + } - // Do not move the task to another stack here. - // This method assumes that the task is already placed in the right stack. - // we do not mess with that decision and we only do the resize! + if (mWindowContainerController == null) { + // Task doesn't exist in window manager yet (e.g. was restored from recents). + // All we can do for now is update the bounds so it can be used when the task is + // added to window manager. + updateOverrideConfiguration(bounds); + if (!inFreeformWindowingMode()) { + // re-restore the task so it can have the proper stack association. + mService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP); + } + return true; + } - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId); + if (!canResizeToBounds(bounds)) { + throw new IllegalArgumentException("resizeTask: Can not resize task=" + this + + " to bounds=" + bounds + " resizeMode=" + mResizeMode); + } - final boolean updatedConfig = updateOverrideConfiguration(bounds); - // This variable holds information whether the configuration didn't change in a significant - // way and the activity was kept the way it was. If it's false, it means the activity had - // to be relaunched due to configuration change. - boolean kept = true; - if (updatedConfig) { - final ActivityRecord r = topRunningActivityLocked(); - if (r != null && !deferResume) { - kept = r.ensureActivityConfigurationLocked(0 /* globalChanges */, preserveWindow); - mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS); - if (!kept) { - mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + // Do not move the task to another stack here. + // This method assumes that the task is already placed in the right stack. + // we do not mess with that decision and we only do the resize! + + Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId); + + final boolean updatedConfig = updateOverrideConfiguration(bounds); + // This variable holds information whether the configuration didn't change in a significant + + // way and the activity was kept the way it was. If it's false, it means the activity + // had + // to be relaunched due to configuration change. + boolean kept = true; + if (updatedConfig) { + final ActivityRecord r = topRunningActivityLocked(); + if (r != null && !deferResume) { + kept = r.ensureActivityConfigurationLocked(0 /* globalChanges */, + preserveWindow); + mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0, + !PRESERVE_WINDOWS); + if (!kept) { + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + } } } - } - mWindowContainerController.resize(mBounds, getOverrideConfiguration(), kept, forced); + mWindowContainerController.resize(kept, forced); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); - return kept; + Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + return kept; + } finally { + mService.mWindowManager.continueSurfaceLayout(); + } } // TODO: Investigate combining with the resize() method above. void resizeWindowContainer() { - mWindowContainerController.resize(mBounds, getOverrideConfiguration(), false /* relayout */, - false /* forced */); + mWindowContainerController.resize(false /* relayout */, false /* forced */); } void getWindowContainerBounds(Rect bounds) { @@ -686,16 +686,17 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi // Make sure the task has the appropriate bounds/size for the stack it is in. final boolean toStackSplitScreenPrimary = toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; + final Rect configBounds = getOverrideBounds(); if ((toStackWindowingMode == WINDOWING_MODE_FULLSCREEN || toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) - && !Objects.equals(mBounds, toStack.mBounds)) { - kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow, + && !Objects.equals(configBounds, toStack.getOverrideBounds())) { + kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow, deferResume); } else if (toStackWindowingMode == WINDOWING_MODE_FREEFORM) { Rect bounds = getLaunchBounds(); if (bounds == null) { mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null); - bounds = mBounds; + bounds = configBounds; } kept = resize(bounds, RESIZE_MODE_FORCED, !mightReplaceWindow, deferResume); } else if (toStackSplitScreenPrimary || toStackWindowingMode == WINDOWING_MODE_PINNED) { @@ -704,7 +705,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi // mode mService.mStackSupervisor.moveRecentsStackToFront(reason); } - kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow, + kept = resize(toStack.getOverrideBounds(), RESIZE_MODE_SYSTEM, !mightReplaceWindow, deferResume); } } finally { @@ -1501,8 +1502,10 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi return true; } final boolean landscape = bounds.width() > bounds.height(); + final Rect configBounds = getOverrideBounds(); if (mResizeMode == RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION) { - return mBounds == null || landscape == (mBounds.width() > mBounds.height()); + return configBounds.isEmpty() + || landscape == (configBounds.width() > configBounds.height()); } return (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY || !landscape) && (mResizeMode != RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY || landscape); @@ -1623,6 +1626,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi final int effectiveRootIndex = findEffectiveRootIndex(); final ActivityRecord r = mActivities.get(effectiveRootIndex); setIntent(r); + + // Update the task description when the activities change + updateTaskDescription(); } void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { @@ -1920,8 +1926,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi return; } + final Rect configBounds = getOverrideBounds(); if (adjustWidth) { - if (mBounds != null && bounds.right == mBounds.right) { + if (!configBounds.isEmpty() && bounds.right == configBounds.right) { bounds.left = bounds.right - minWidth; } else { // Either left bounds match, or neither match, or the previous bounds were @@ -1930,7 +1937,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } } if (adjustHeight) { - if (mBounds != null && bounds.bottom == mBounds.bottom) { + if (!configBounds.isEmpty() && bounds.bottom == configBounds.bottom) { bounds.top = bounds.bottom - minHeight; } else { // Either top bounds match, or neither match, or the previous bounds were @@ -1977,38 +1984,37 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi * @return True if the override configuration was updated. */ boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) { - if (Objects.equals(mBounds, bounds)) { + if (equivalentOverrideBounds(bounds)) { return false; } + final Rect currentBounds = getOverrideBounds(); + mTmpConfig.setTo(getOverrideConfiguration()); - final boolean oldFullscreen = mFullscreen; + final boolean oldMatchParentBounds = matchParentBounds(); final Configuration newConfig = getOverrideConfiguration(); - mFullscreen = bounds == null; + final boolean matchParentBounds = bounds == null || bounds.isEmpty(); final boolean persistBounds = getWindowConfiguration().persistTaskBounds(); - if (mFullscreen) { - if (mBounds != null && persistBounds) { - mLastNonFullscreenBounds = mBounds; + if (matchParentBounds) { + if (!currentBounds.isEmpty() && persistBounds) { + mLastNonFullscreenBounds = currentBounds; } - mBounds = null; + setBounds(null); newConfig.unset(); } else { mTmpRect.set(bounds); adjustForMinimalTaskDimensions(mTmpRect); - if (mBounds == null) { - mBounds = new Rect(mTmpRect); - } else { - mBounds.set(mTmpRect); - } + setBounds(mTmpRect); + if (mStack == null || persistBounds) { - mLastNonFullscreenBounds = mBounds; + mLastNonFullscreenBounds = getOverrideBounds(); } computeOverrideConfiguration(newConfig, mTmpRect, insetBounds, mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom); } onOverrideConfigurationChanged(newConfig); - if (mFullscreen != oldFullscreen) { + if (matchParentBounds != oldMatchParentBounds) { mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this); } @@ -2053,23 +2059,19 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi final int longSize = Math.max(compatScreenHeightDp, compatScreenWidthDp); final int shortSize = Math.min(compatScreenHeightDp, compatScreenWidthDp); config.screenLayout = Configuration.reduceScreenLayout(sl, longSize, shortSize); - } Rect updateOverrideConfigurationFromLaunchBounds() { - final Rect bounds = validateBounds(getLaunchBounds()); + final Rect bounds = getLaunchBounds(); updateOverrideConfiguration(bounds); - if (bounds != null) { - bounds.set(mBounds); + if (bounds != null && !bounds.isEmpty()) { + // TODO: Review if we actually want to do this - we are setting the launch bounds + // directly here. + bounds.set(getOverrideBounds()); } return bounds; } - static Rect validateBounds(Rect bounds) { - // TODO: Not needed once we have bounds in WindowConfiguration. - return (bounds != null && bounds.isEmpty()) ? null : bounds; - } - /** Updates the task's bounds and override configuration to match what is expected for the * input stack. */ void updateOverrideConfigurationForStack(ActivityStack inStack) { @@ -2082,7 +2084,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi throw new IllegalArgumentException("Can not position non-resizeable task=" + this + " in stack=" + inStack); } - if (mBounds != null) { + if (!matchParentBounds()) { return; } if (mLastNonFullscreenBounds != null) { @@ -2091,7 +2093,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(this, null); } } else { - updateOverrideConfiguration(inStack.mBounds); + updateOverrideConfiguration(inStack.getOverrideBounds()); } } @@ -2105,9 +2107,9 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi if (!isActivityTypeStandardOrUndefined() || windowingMode == WINDOWING_MODE_FULLSCREEN || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) { - return isResizeable() ? mStack.mBounds : null; + return isResizeable() ? mStack.getOverrideBounds() : null; } else if (!getWindowConfiguration().persistTaskBounds()) { - return mStack.mBounds; + return mStack.getOverrideBounds(); } return mLastNonFullscreenBounds; } @@ -2287,9 +2289,12 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi } proto.write(ACTIVITY_TYPE, getActivityType()); proto.write(RESIZE_MODE, mResizeMode); - proto.write(FULLSCREEN, mFullscreen); - if (mBounds != null) { - mBounds.writeToProto(proto, BOUNDS); + // TODO: Remove, no longer needed with windowingMode. + proto.write(FULLSCREEN, matchParentBounds()); + + if (!matchParentBounds()) { + final Rect bounds = getOverrideBounds(); + bounds.writeToProto(proto, BOUNDS); } proto.write(MIN_WIDTH, mMinWidth); proto.write(MIN_HEIGHT, mMinHeight); diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index ae01ea5721d8..483d5181f9c9 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -217,13 +217,6 @@ public class BatterySaverController implements BatterySaverPolicyListener { pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0); } - if (enabled) { - // STOPSHIP Remove the toast. - Toast.makeText(mContext, - com.android.internal.R.string.battery_saver_warning, - Toast.LENGTH_LONG).show(); - } - if (ArrayUtils.isEmpty(fileValues)) { mFileUpdater.restoreDefault(); } else { @@ -231,6 +224,13 @@ public class BatterySaverController implements BatterySaverPolicyListener { } if (sendBroadcast) { + if (enabled) { + // STOPSHIP Remove the toast. + Toast.makeText(mContext, + com.android.internal.R.string.battery_saver_warning, + Toast.LENGTH_LONG).show(); + } + if (DEBUG) { Slog.i(TAG, "Sending broadcasts for mode: " + enabled); } diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index a528ecdf537e..00a0d3d18f9c 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -181,13 +181,12 @@ public class AppWindowContainerController IApplicationToken token, AppWindowContainerListener listener, int index, int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable, - int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos, - Rect bounds) { + int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos) { this(taskController, token, listener, index, requestedOrientation, fullscreen, showForAllUsers, configChanges, voiceInteraction, launchTaskBehind, alwaysFocusable, targetSdkVersion, rotationAnimationHint, inputDispatchingTimeoutNanos, - WindowManagerService.getInstance(), bounds); + WindowManagerService.getInstance()); } public AppWindowContainerController(TaskWindowContainerController taskController, @@ -195,7 +194,7 @@ public class AppWindowContainerController int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int configChanges, boolean voiceInteraction, boolean launchTaskBehind, boolean alwaysFocusable, int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos, - WindowManagerService service, Rect bounds) { + WindowManagerService service) { super(listener, service); mHandler = new H(service.mH.getLooper()); mToken = token; @@ -216,7 +215,7 @@ public class AppWindowContainerController atoken = createAppWindow(mService, token, voiceInteraction, task.getDisplayContent(), inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdkVersion, requestedOrientation, rotationAnimationHint, configChanges, launchTaskBehind, - alwaysFocusable, this, bounds); + alwaysFocusable, this); if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addAppToken: " + atoken + " controller=" + taskController + " at " + index); task.addChild(atoken, index); @@ -228,11 +227,11 @@ public class AppWindowContainerController boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) { + boolean alwaysFocusable, AppWindowContainerController controller) { return new AppWindowToken(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller, bounds); + controller); } public void removeContainer(int displayId) { @@ -299,17 +298,6 @@ public class AppWindowContainerController } } - // TODO(b/36505427): Maybe move to WindowContainerController so other sub-classes can use it as - // a generic way to set override config. Need to untangle current ways the override config is - // currently set for tasks and displays before we are doing that though. - public void onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds) { - synchronized(mWindowMap) { - if (mContainer != null) { - mContainer.onOverrideConfigurationChanged(overrideConfiguration, bounds); - } - } - } - public void setDisablePreviewScreenshots(boolean disable) { synchronized (mWindowMap) { if (mContainer == null) { diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 627f8cee3fd1..c39ce982e4cb 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -175,11 +175,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree private boolean mLastContainsShowWhenLockedWindow; private boolean mLastContainsDismissKeyguardWindow; - // The bounds of this activity. Mainly used for aspect-ratio compatibility. - // TODO(b/36505427): Every level on WindowContainer now has bounds information, which directly - // affects the configuration. We should probably move this into that class. - private final Rect mBounds = new Rect(); - ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>(); ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>(); @@ -196,8 +191,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, boolean alwaysFocusable, - AppWindowContainerController controller, Rect bounds) { - this(service, token, voiceInteraction, dc, fullscreen, bounds); + AppWindowContainerController controller) { + this(service, token, voiceInteraction, dc, fullscreen); setController(controller); mInputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; mShowForAllUsers = showForAllUsers; @@ -214,7 +209,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, - DisplayContent dc, boolean fillsParent, Rect bounds) { + DisplayContent dc, boolean fillsParent) { super(service, token != null ? token.asBinder() : null, TYPE_APPLICATION, true, dc, false /* ownerCanManageAppTokens */); appToken = token; @@ -222,27 +217,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mFillsParent = fillsParent; mInputApplicationHandle = new InputApplicationHandle(this); mAppAnimator = new AppWindowAnimator(this, service); - if (bounds != null) { - mBounds.set(bounds); - } - } - - void onOverrideConfigurationChanged(Configuration overrideConfiguration, Rect bounds) { - onOverrideConfigurationChanged(overrideConfiguration); - if (mBounds.equals(bounds)) { - return; - } - // TODO(b/36505427): If bounds is in WC, then we can automatically call onResize() when set. - mBounds.set(bounds); - onResize(); - } - - void getBounds(Rect outBounds) { - outBounds.set(mBounds); - } - - boolean hasBounds() { - return !mBounds.isEmpty(); } void onFirstWindowDrawn(WindowState win, WindowStateAnimator winAnimator) { diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index b534b8ac2244..d340923b754e 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -36,6 +36,7 @@ import static com.android.server.wm.proto.ConfigurationContainerProto.OVERRIDE_C import android.annotation.CallSuper; import android.app.WindowConfiguration; import android.content.res.Configuration; +import android.graphics.Rect; import android.util.proto.ProtoOutputStream; import java.io.PrintWriter; @@ -46,6 +47,11 @@ import java.util.ArrayList; * hierarchy. */ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { + /** + * {@link #Rect} returned from {@link #getOverrideBounds()} to prevent original value from being + * set directly. + */ + private Rect mReturnBounds = new Rect(); /** Contains override configuration settings applied to this configuration container. */ private Configuration mOverrideConfiguration = new Configuration(); @@ -71,6 +77,16 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { // TODO: Can't have ag/2592611 soon enough! private final Configuration mTmpConfig = new Configuration(); + // Used for setting bounds + private final Rect mTmpRect = new Rect(); + + static final int BOUNDS_CHANGE_NONE = 0; + // Return value from {@link setBounds} indicating the position of the override bounds changed. + static final int BOUNDS_CHANGE_POSITION = 1; + // Return value from {@link setBounds} indicating the size of the override bounds changed. + static final int BOUNDS_CHANGE_SIZE = 1 << 1; + + /** * Returns full configuration applied to this configuration container. * This method should be used for getting settings applied in each particular level of the @@ -148,6 +164,118 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { } } + /** + * Indicates whether this container has not specified any bounds different from its parent. In + * this case, it will inherit the bounds of the first ancestor which specifies a bounds. + * @return {@code true} if no explicit bounds have been set at this container level. + * {@code false} otherwise. + */ + public boolean matchParentBounds() { + return getOverrideBounds().isEmpty(); + } + + /** + * Returns whether the bounds specified is considered the same as the existing override bounds. + * This is either when the two bounds are equal or the override bounds is empty and the + * specified bounds is null. + * + * @return {@code true} if the bounds are equivalent, {@code false} otherwise + */ + public boolean equivalentOverrideBounds(Rect bounds) { + return equivalentBounds(getOverrideBounds(), bounds); + } + + /** + * Returns whether the two bounds are equal to each other or are a combination of null or empty. + */ + public static boolean equivalentBounds(Rect bounds, Rect other) { + return bounds == other + || (bounds != null && (bounds.equals(other) || (bounds.isEmpty() && other == null))) + || (other != null && other.isEmpty() && bounds == null); + } + + /** + * Returns the effective bounds of this container, inheriting the first non-empty bounds set in + * its ancestral hierarchy, including itself. + * @return + */ + public Rect getBounds() { + mReturnBounds.set(getConfiguration().windowConfiguration.getBounds()); + return mReturnBounds; + } + + public void getBounds(Rect outBounds) { + outBounds.set(getBounds()); + } + + /** + * Returns the current bounds explicitly set on this container. The {@link Rect} handed back is + * shared for all calls to this method and should not be modified. + */ + public Rect getOverrideBounds() { + mReturnBounds.set(getOverrideConfiguration().windowConfiguration.getBounds()); + + return mReturnBounds; + } + + /** + * Sets the passed in {@link Rect} to the current bounds. + * @see {@link #getOverrideBounds()}. + */ + public void getOverrideBounds(Rect outBounds) { + outBounds.set(getOverrideBounds()); + } + + /** + * Sets the bounds at the current hierarchy level, overriding any bounds set on an ancestor. + * This value will be reported when {@link #getBounds()} and {@link #getOverrideBounds()}. If + * an empty {@link Rect} or null is specified, this container will be considered to match its + * parent bounds {@see #matchParentBounds} and will inherit bounds from its parent. + * @param bounds The bounds defining the container size. + * @return a bitmask representing the types of changes made to the bounds. + */ + public int setBounds(Rect bounds) { + int boundsChange = diffOverrideBounds(bounds); + + if (boundsChange == BOUNDS_CHANGE_NONE) { + return boundsChange; + } + + + mTmpConfig.setTo(getOverrideConfiguration()); + mTmpConfig.windowConfiguration.setBounds(bounds); + onOverrideConfigurationChanged(mTmpConfig); + + return boundsChange; + } + + public int setBounds(int left, int top, int right, int bottom) { + mTmpRect.set(left, top, right, bottom); + return setBounds(mTmpRect); + } + + int diffOverrideBounds(Rect bounds) { + if (equivalentOverrideBounds(bounds)) { + return BOUNDS_CHANGE_NONE; + } + + int boundsChange = BOUNDS_CHANGE_NONE; + + final Rect existingBounds = getOverrideBounds(); + + if (bounds == null || existingBounds.left != bounds.left + || existingBounds.top != bounds.top) { + boundsChange |= BOUNDS_CHANGE_POSITION; + } + + if (bounds == null || existingBounds.width() != bounds.width() + || existingBounds.height() != bounds.height()) { + boundsChange |= BOUNDS_CHANGE_SIZE; + } + + return boundsChange; + } + public WindowConfiguration getWindowConfiguration() { return mFullConfiguration.windowConfiguration; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 44dfa07794d2..56e592208eb6 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -181,8 +181,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(); // Contains all non-app window containers that should be displayed above the app containers // (e.g. Status bar) - private final NonAppWindowContainers mAboveAppWindowsContainers = - new NonAppWindowContainers("mAboveAppWindowsContainers"); + private final AboveAppWindowContainers mAboveAppWindowsContainers = + new AboveAppWindowContainers("mAboveAppWindowsContainers"); // Contains all non-app window containers that should be displayed below the app containers // (e.g. Wallpaper). private final NonAppWindowContainers mBelowAppWindowsContainers = @@ -314,6 +314,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo private final Matrix mTmpMatrix = new Matrix(); private final Region mTmpRegion = new Region(); + /** Used for handing back size of display */ + private final Rect mTmpBounds = new Rect(); + WindowManagerService mService; /** Remove this display when animation on it has completed. */ @@ -353,7 +356,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo /** * We organize all top-level Surfaces in to the following layers. * mOverlayLayer contains a few Surfaces which are always on top of others - * and omitted from Screen-Magnification ({@link WindowState#isScreenOverlay}) + * and omitted from Screen-Magnification, for example the strict mode flash or + * the magnification overlay itself. * {@link #mWindowingLayer} contains everything else. */ private SurfaceControl mOverlayLayer; @@ -1223,6 +1227,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics, mCompatDisplayMetrics); } + + updateBounds(); return mDisplayInfo; } @@ -1541,8 +1547,17 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // See {@link PhoneWindowManager#setInitialDisplaySize}...sigh... mService.reconfigureDisplayLocked(this); - getDockedDividerController().onConfigurationChanged(); - getPinnedStackController().onConfigurationChanged(); + final DockedStackDividerController dividerController = getDockedDividerController(); + + if (dividerController != null) { + getDockedDividerController().onConfigurationChanged(); + } + + final PinnedStackController pinnedStackController = getPinnedStackController(); + + if (pinnedStackController != null) { + getPinnedStackController().onConfigurationChanged(); + } } /** @@ -1681,33 +1696,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; } - void getLogicalDisplayRect(Rect out) { - // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked. - final int orientation = mDisplayInfo.rotation; - boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270); - final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth; - final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; - int width = mDisplayInfo.logicalWidth; - int left = (physWidth - width) / 2; - int height = mDisplayInfo.logicalHeight; - int top = (physHeight - height) / 2; - out.set(left, top, left + width, top + height); - } - - private void getLogicalDisplayRect(Rect out, int orientation) { - getLogicalDisplayRect(out); - - // Rotate the Rect if needed. - final int currentRotation = mDisplayInfo.rotation; - final int rotationDelta = deltaRotation(currentRotation, orientation); - if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) { - createRotationMatrix(rotationDelta, mBaseDisplayWidth, mBaseDisplayHeight, mTmpMatrix); - mTmpRectF.set(out); - mTmpMatrix.mapRect(mTmpRectF); - mTmpRectF.round(out); - } - } - /** * If display metrics changed, overrides are not set and it's not just a rotation - update base * values. @@ -1775,6 +1763,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight); + + updateBounds(); } void getContentRect(Rect out) { @@ -2104,7 +2094,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } void rotateBounds(int oldRotation, int newRotation, Rect bounds) { - getLogicalDisplayRect(mTmpRect, newRotation); + getBounds(mTmpRect, newRotation); // Compute a transform matrix to undo the coordinate space transformation, // and present the window at the same physical position it previously occupied. @@ -2881,6 +2871,44 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return mTmpApplySurfaceChangesTransactionState.focusDisplayed; } + private void updateBounds() { + calculateBounds(mTmpBounds); + setBounds(mTmpBounds); + } + + // Determines the current display bounds based on the current state + private void calculateBounds(Rect out) { + // Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked. + final int orientation = mDisplayInfo.rotation; + boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270); + final int physWidth = rotated ? mBaseDisplayHeight : mBaseDisplayWidth; + final int physHeight = rotated ? mBaseDisplayWidth : mBaseDisplayHeight; + int width = mDisplayInfo.logicalWidth; + int left = (physWidth - width) / 2; + int height = mDisplayInfo.logicalHeight; + int top = (physHeight - height) / 2; + out.set(left, top, left + width, top + height); + } + + @Override + public void getBounds(Rect out) { + calculateBounds(out); + } + + private void getBounds(Rect out, int orientation) { + getBounds(out); + + // Rotate the Rect if needed. + final int currentRotation = mDisplayInfo.rotation; + final int rotationDelta = deltaRotation(currentRotation, orientation); + if (rotationDelta == ROTATION_90 || rotationDelta == ROTATION_270) { + createRotationMatrix(rotationDelta, mBaseDisplayWidth, mBaseDisplayHeight, mTmpMatrix); + mTmpRectF.set(out); + mTmpMatrix.mapRect(mTmpRectF); + mTmpRectF.round(out); + } + } + void performLayout(boolean initial, boolean updateInputWindows) { if (!isLayoutNeeded()) { return; @@ -3719,11 +3747,39 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } + private final class AboveAppWindowContainers extends NonAppWindowContainers { + AboveAppWindowContainers(String name) { + super(name); + } + + void assignChildLayers(SurfaceControl.Transaction t, WindowContainer imeContainer) { + boolean needAssignIme = imeContainer != null + && imeContainer.getSurfaceControl() != null; + for (int j = 0; j < mChildren.size(); ++j) { + final WindowToken wt = mChildren.get(j); + wt.assignLayer(t, j); + wt.assignChildLayers(t); + + int layer = mService.mPolicy.getWindowLayerFromTypeLw( + wt.windowType, wt.mOwnerCanManageAppTokens); + if (needAssignIme && layer >= TYPE_INPUT_METHOD_DIALOG) { + t.setRelativeLayer(imeContainer.getSurfaceControl(), + wt.getSurfaceControl(), -1); + needAssignIme = false; + } + } + if (needAssignIme) { + t.setRelativeLayer(imeContainer.getSurfaceControl(), + getSurfaceControl(), Integer.MIN_VALUE); + } + } + } + /** * Window container class that contains all containers on this display that are not related to * Apps. E.g. status bar. */ - private final class NonAppWindowContainers extends DisplayChildWindowContainer<WindowToken> { + private class NonAppWindowContainers extends DisplayChildWindowContainer<WindowToken> { /** * Compares two child window tokens returns -1 if the first is lesser than the second in * terms of z-order and 1 otherwise. @@ -3821,12 +3877,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return b; } - b.setName(child.getName()); - if (child.isScreenOverlay()) { - return b.setParent(mOverlayLayer); - } else { - return b.setParent(mWindowingLayer); - } + return b.setName(child.getName()) + .setParent(mWindowingLayer); } /** @@ -3864,26 +3916,36 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mAboveAppWindowsContainers.assignLayer(t, 2); WindowState imeTarget = mService.mInputMethodTarget; - if (imeTarget == null || imeTarget.inSplitScreenWindowingMode()) { - // In split-screen windowing mode we can't layer the - // IME relative to the IME target because it needs to - // go over the docked divider, so instead we place it on top - // of everything and use relative layering of windows which need - // to go above it (see special logic in WindowState#assignLayer) - mImeWindowsContainers.assignLayer(t, 3); - } else { + boolean needAssignIme = true; + + // In the case where we have an IME target that is not in split-screen + // mode IME assignment is easy. We just need the IME to go directly above + // the target. This way children of the target will naturally go above the IME + // and everyone is happy. + // + // In the case of split-screen windowing mode, we need to elevate the IME above the + // docked divider while keeping the app itself below the docked divider, so instead + // we use relative layering of the IME targets child windows, and place the + // IME in the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}). + // + // In the case where we have no IME target we assign it where it's base layer would + // place it in the AboveAppWindowContainers. + if (imeTarget != null && !imeTarget.inSplitScreenWindowingMode() + && (imeTarget.getSurfaceControl() != null)) { t.setRelativeLayer(mImeWindowsContainers.getSurfaceControl(), imeTarget.getSurfaceControl(), // TODO: We need to use an extra level on the app surface to ensure // this is always above SurfaceView but always below attached window. 1); + needAssignIme = false; } // Above we have assigned layers to our children, now we ask them to assign // layers to their children. mBelowAppWindowsContainers.assignChildLayers(t); mTaskStackContainers.assignChildLayers(t); - mAboveAppWindowsContainers.assignChildLayers(t); + mAboveAppWindowsContainers.assignChildLayers(t, + needAssignIme == true ? mImeWindowsContainers : null); mImeWindowsContainers.assignChildLayers(t); } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 8308417fa141..a37598e706c6 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -655,8 +655,8 @@ public class DockedStackDividerController { } private boolean isWithinDisplay(Task task) { - task.mStack.getBounds(mTmpRect); - mDisplayContent.getLogicalDisplayRect(mTmpRect2); + task.getBounds(mTmpRect); + mDisplayContent.getBounds(mTmpRect2); return mTmpRect.intersect(mTmpRect2); } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index e81d366bd85a..112e62f27ea2 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -645,15 +645,15 @@ class DragState { try (final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) { transaction.setPosition( mSurfaceControl, - (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_X), - (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_Y)); + (float) animation.getAnimatedValue(ANIMATED_PROPERTY_X), + (float) animation.getAnimatedValue(ANIMATED_PROPERTY_Y)); transaction.setAlpha( mSurfaceControl, - (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_ALPHA)); + (float) animation.getAnimatedValue(ANIMATED_PROPERTY_ALPHA)); transaction.setMatrix( mSurfaceControl, - (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE), 0, - 0, (float) mAnimator.getAnimatedValue(ANIMATED_PROPERTY_SCALE)); + (float) animation.getAnimatedValue(ANIMATED_PROPERTY_SCALE), 0, + 0, (float) animation.getAnimatedValue(ANIMATED_PROPERTY_SCALE)); transaction.apply(); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 18b0f976dbd2..7e29a3aa811e 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -661,8 +661,8 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { if (w.inPinnedWindowingMode()) { if (mAddPipInputConsumerHandle && (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) { - // Update the bounds of the Pip input consumer to match the Pinned stack - w.getStack().getBounds(pipTouchableBounds); + // Update the bounds of the Pip input consumer to match the window bounds. + w.getBounds(pipTouchableBounds); pipInputConsumer.mWindowHandle.touchableRegion.set(pipTouchableBounds); addInputWindowHandle(pipInputConsumer.mWindowHandle); mAddPipInputConsumerHandle = false; diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java index 41f076d7b6af..b021a7223e2e 100644 --- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java +++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java @@ -106,7 +106,7 @@ public class PinnedStackWindowController extends StackWindowController { } else { // Otherwise, use the display bounds toBounds = new Rect(); - mContainer.getDisplayContent().getLogicalDisplayRect(toBounds); + mContainer.getDisplayContent().getBounds(toBounds); } } else if (fromFullscreen) { schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 70bf15ceb4c3..5a39de5c3242 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -28,7 +28,6 @@ import static com.android.server.wm.proto.ScreenRotationAnimationProto.STARTED; import android.content.Context; import android.graphics.Matrix; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.util.Slog; import android.util.proto.ProtoOutputStream; @@ -230,7 +229,7 @@ class ScreenRotationAnimation { mService = service; mContext = context; mDisplayContent = displayContent; - displayContent.getLogicalDisplayRect(mOriginalDisplayRect); + displayContent.getBounds(mOriginalDisplayRect); // Screenshot does NOT include rotation! final Display display = displayContent.getDisplay(); @@ -312,7 +311,7 @@ class ScreenRotationAnimation { float x = mTmpFloats[Matrix.MTRANS_X]; float y = mTmpFloats[Matrix.MTRANS_Y]; if (mForceDefaultOrientation) { - mDisplayContent.getLogicalDisplayRect(mCurrentDisplayRect); + mDisplayContent.getBounds(mCurrentDisplayRect); x -= mCurrentDisplayRect.left; y -= mCurrentDisplayRect.top; } diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java index 95c1d536123f..c2a4be55e30a 100644 --- a/services/core/java/com/android/server/wm/StackWindowController.java +++ b/services/core/java/com/android/server/wm/StackWindowController.java @@ -111,8 +111,7 @@ public class StackWindowController } } - public void positionChildAt(TaskWindowContainerController child, int position, Rect bounds, - Configuration overrideConfig) { + public void positionChildAt(TaskWindowContainerController child, int position) { synchronized (mWindowMap) { if (DEBUG_STACK) Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position); @@ -126,7 +125,7 @@ public class StackWindowController "positionChildAt: could not find stack for task=" + mContainer); return; } - child.mContainer.positionAt(position, bounds, overrideConfig); + child.mContainer.positionAt(position); mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); } } @@ -178,24 +177,22 @@ public class StackWindowController * Re-sizes a stack and its containing tasks. * * @param bounds New stack bounds. Passing in null sets the bounds to fullscreen. - * @param configs Configurations for tasks in the resized stack, keyed by task id. * @param taskBounds Bounds for tasks in the resized stack, keyed by task id. - * @return True if the stack is now fullscreen. + * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id. */ - public boolean resize(Rect bounds, SparseArray<Configuration> configs, - SparseArray<Rect> taskBounds, SparseArray<Rect> taskTempInsetBounds) { + public void resize(Rect bounds, SparseArray<Rect> taskBounds, + SparseArray<Rect> taskTempInsetBounds) { synchronized (mWindowMap) { if (mContainer == null) { throw new IllegalArgumentException("resizeStack: stack " + this + " not found."); } // We might trigger a configuration change. Save the current task bounds for freezing. mContainer.prepareFreezingTaskBounds(); - if (mContainer.setBounds(bounds, configs, taskBounds, taskTempInsetBounds) + if (mContainer.setBounds(bounds, taskBounds, taskTempInsetBounds) && mContainer.isVisible()) { mContainer.getDisplayContent().setLayoutNeeded(); mService.mWindowPlacerLocked.performSurfacePlacement(); } - return mContainer.getRawFullscreen(); } } @@ -227,7 +224,7 @@ public class StackWindowController public void getRawBounds(Rect outBounds) { synchronized (mWindowMap) { - if (mContainer.getRawFullscreen()) { + if (mContainer.matchParentBounds()) { outBounds.setEmpty(); } else { mContainer.getRawBounds(outBounds); @@ -275,6 +272,7 @@ public class StackWindowController final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds(); + config.windowConfiguration.setBounds(bounds); config.windowConfiguration.setAppBounds(!bounds.isEmpty() ? bounds : null); boolean intersectParentBounds = false; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f70845e52bb9..8aa129a45373 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -53,12 +53,6 @@ import java.util.function.Consumer; class Task extends WindowContainer<AppWindowToken> { static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM; - // Return value from {@link setBounds} indicating no change was made to the Task bounds. - private static final int BOUNDS_CHANGE_NONE = 0; - // Return value from {@link setBounds} indicating the position of the Task bounds changed. - private static final int BOUNDS_CHANGE_POSITION = 1; - // Return value from {@link setBounds} indicating the size of the Task bounds changed. - private static final int BOUNDS_CHANGE_SIZE = 1 << 1; // TODO: Track parent marks like this in WindowContainer. TaskStack mStack; @@ -67,8 +61,6 @@ class Task extends WindowContainer<AppWindowToken> { private boolean mDeferRemoval = false; final WindowManagerService mService; - // Content limits relative to the DisplayContent this sits in. - private Rect mBounds = new Rect(); final Rect mPreparedFrozenBounds = new Rect(); final Configuration mPreparedFrozenMergedConfig = new Configuration(); @@ -78,13 +70,12 @@ class Task extends WindowContainer<AppWindowToken> { // Device rotation as of the last time {@link #mBounds} was set. private int mRotation; - // Whether mBounds is fullscreen - private boolean mFillsParent = true; - // For comparison with DisplayContent bounds. private Rect mTmpRect = new Rect(); // For handling display rotations. private Rect mTmpRect2 = new Rect(); + // For retrieving dim bounds + private Rect mTmpRect3 = new Rect(); // Resize mode of the task. See {@link ActivityInfo#resizeMode} private int mResizeMode; @@ -108,8 +99,8 @@ class Task extends WindowContainer<AppWindowToken> { private Dimmer mDimmer = new Dimmer(this); private final Rect mTmpDimBoundsRect = new Rect(); - Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, - int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription, + Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode, + boolean supportsPictureInPicture, TaskDescription taskDescription, TaskWindowContainerController controller) { mTaskId = taskId; mStack = stack; @@ -118,7 +109,7 @@ class Task extends WindowContainer<AppWindowToken> { mResizeMode = resizeMode; mSupportsPictureInPicture = supportsPictureInPicture; setController(controller); - setBounds(bounds, getOverrideConfiguration()); + setBounds(getOverrideBounds()); mTaskDescription = taskDescription; // Tasks have no set orientation value (including SCREEN_ORIENTATION_UNSPECIFIED). @@ -227,9 +218,8 @@ class Task extends WindowContainer<AppWindowToken> { } /** @see com.android.server.am.ActivityManagerService#positionTaskInStack(int, int, int). */ - void positionAt(int position, Rect bounds, Configuration overrideConfig) { + void positionAt(int position) { mStack.positionChildAt(position, this, false /* includingParents */); - resizeLocked(bounds, overrideConfig, false /* force */); } @Override @@ -271,48 +261,37 @@ class Task extends WindowContainer<AppWindowToken> { } } - /** Set the task bounds. Passing in null sets the bounds to fullscreen. */ - // TODO: There is probably not a need to pass in overrideConfig anymore since any change to it - // will be automatically propagated from the AM. Also, mBound is going to be in - // WindowConfiguration long term. - private int setBounds(Rect bounds, Configuration overrideConfig) { - if (overrideConfig == null) { - overrideConfig = EMPTY; + public int setBounds(Rect bounds, boolean forceResize) { + final int boundsChanged = setBounds(bounds); + + if (forceResize && (boundsChanged & BOUNDS_CHANGE_SIZE) != BOUNDS_CHANGE_SIZE) { + onResize(); + return BOUNDS_CHANGE_SIZE | boundsChanged; } - boolean oldFullscreen = mFillsParent; + return boundsChanged; + } + + /** Set the task bounds. Passing in null sets the bounds to fullscreen. */ + @Override + public int setBounds(Rect bounds) { int rotation = Surface.ROTATION_0; final DisplayContent displayContent = mStack.getDisplayContent(); if (displayContent != null) { - displayContent.getLogicalDisplayRect(mTmpRect); rotation = displayContent.getDisplayInfo().rotation; - mFillsParent = bounds == null; - if (mFillsParent) { - bounds = mTmpRect; - } - } - - if (bounds == null) { + } else if (bounds == null) { // Can't set to fullscreen if we don't have a display to get bounds from... return BOUNDS_CHANGE_NONE; } - if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) { - return BOUNDS_CHANGE_NONE; - } - int boundsChange = BOUNDS_CHANGE_NONE; - if (mBounds.left != bounds.left || mBounds.top != bounds.top) { - boundsChange |= BOUNDS_CHANGE_POSITION; - } - if (mBounds.width() != bounds.width() || mBounds.height() != bounds.height()) { - boundsChange |= BOUNDS_CHANGE_SIZE; + if (equivalentOverrideBounds(bounds)) { + return BOUNDS_CHANGE_NONE; } - mBounds.set(bounds); + final int boundsChange = super.setBounds(bounds); mRotation = rotation; - onOverrideConfigurationChanged(overrideConfig); return boundsChange; } @@ -360,28 +339,12 @@ class Task extends WindowContainer<AppWindowToken> { return isResizeable(); } - boolean resizeLocked(Rect bounds, Configuration overrideConfig, boolean forced) { - int boundsChanged = setBounds(bounds, overrideConfig); - if (forced) { - boundsChanged |= BOUNDS_CHANGE_SIZE; - } - if (boundsChanged == BOUNDS_CHANGE_NONE) { - return false; - } - if ((boundsChanged & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) { - onResize(); - } else { - onMovedByResize(); - } - return true; - } - /** * Prepares the task bounds to be frozen with the current size. See * {@link AppWindowToken#freezeBounds}. */ void prepareFreezingBounds() { - mPreparedFrozenBounds.set(mBounds); + mPreparedFrozenBounds.set(getBounds()); mPreparedFrozenMergedConfig.setTo(getConfiguration()); } @@ -407,30 +370,30 @@ class Task extends WindowContainer<AppWindowToken> { mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top); } setTempInsetBounds(tempInsetBounds); - resizeLocked(mTmpRect2, getOverrideConfiguration(), false /* forced */); + setBounds(mTmpRect2, false /* forced */); } /** Return true if the current bound can get outputted to the rest of the system as-is. */ private boolean useCurrentBounds() { final DisplayContent displayContent = getDisplayContent(); - return mFillsParent + return matchParentBounds() || !inSplitScreenSecondaryWindowingMode() || displayContent == null || displayContent.getSplitScreenPrimaryStackIgnoringVisibility() != null; } - /** Original bounds of the task if applicable, otherwise fullscreen rect. */ - void getBounds(Rect out) { + @Override + public void getBounds(Rect out) { if (useCurrentBounds()) { // No need to adjust the output bounds if fullscreen or the docked stack is visible // since it is already what we want to represent to the rest of the system. - out.set(mBounds); + super.getBounds(out); return; } // The bounds has been adjusted to accommodate for a docked stack, but the docked stack is // not currently visible. Go ahead a represent it as fullscreen to the rest of the system. - mStack.getDisplayContent().getLogicalDisplayRect(out); + mStack.getDisplayContent().getBounds(out); } /** @@ -490,7 +453,7 @@ class Task extends WindowContainer<AppWindowToken> { return; } - if (!mFillsParent) { + if (!matchParentBounds()) { // When minimizing the docked stack when going home, we don't adjust the task bounds // so we need to intersect the task bounds with the stack bounds here. // @@ -501,11 +464,11 @@ class Task extends WindowContainer<AppWindowToken> { mStack.getBounds(out); } else { mStack.getBounds(mTmpRect); - mTmpRect.intersect(mBounds); + mTmpRect.intersect(getBounds()); } out.set(mTmpRect); } else { - out.set(mBounds); + out.set(getBounds()); } return; } @@ -513,7 +476,7 @@ class Task extends WindowContainer<AppWindowToken> { // The bounds has been adjusted to accommodate for a docked stack, but the docked stack is // not currently visible. Go ahead a represent it as fullscreen to the rest of the system. if (displayContent != null) { - displayContent.getLogicalDisplayRect(out); + displayContent.getBounds(out); } } @@ -541,10 +504,10 @@ class Task extends WindowContainer<AppWindowToken> { if (displayContent == null) { return; } - if (mFillsParent) { + if (matchParentBounds()) { // TODO: Yeah...not sure if this works with WindowConfiguration, but shouldn't be a // problem once we move mBounds into WindowConfiguration. - setBounds(null, getOverrideConfiguration()); + setBounds(null); return; } final int newRotation = displayContent.getDisplayInfo().rotation; @@ -557,18 +520,18 @@ class Task extends WindowContainer<AppWindowToken> { // task bounds so it stays in the same place. // - Rotate the bounds and notify activity manager if the task can be resized independently // from its stack. The stack will take care of task rotation for the other case. - mTmpRect2.set(mBounds); + mTmpRect2.set(getBounds()); if (!getWindowConfiguration().canResizeTask()) { - setBounds(mTmpRect2, getOverrideConfiguration()); + setBounds(mTmpRect2); return; } displayContent.rotateBounds(mRotation, newRotation, mTmpRect2); - if (setBounds(mTmpRect2, getOverrideConfiguration()) != BOUNDS_CHANGE_NONE) { + if (setBounds(mTmpRect2) != BOUNDS_CHANGE_NONE) { final TaskWindowContainerController controller = getController(); if (controller != null) { - controller.requestResize(mBounds, RESIZE_MODE_SYSTEM_SCREEN_ROTATION); + controller.requestResize(getBounds(), RESIZE_MODE_SYSTEM_SCREEN_ROTATION); } } } @@ -632,7 +595,7 @@ class Task extends WindowContainer<AppWindowToken> { boolean isFullscreen() { if (useCurrentBounds()) { - return mFillsParent; + return matchParentBounds(); } // The bounds has been adjusted to accommodate for a docked stack, but the docked stack // is not currently visible. Go ahead a represent it as fullscreen to the rest of the @@ -661,7 +624,7 @@ class Task extends WindowContainer<AppWindowToken> { @Override boolean fillsParent() { - return mFillsParent || !getWindowConfiguration().canResizeTask(); + return matchParentBounds() || !getWindowConfiguration().canResizeTask(); } @Override @@ -711,8 +674,8 @@ class Task extends WindowContainer<AppWindowToken> { final AppWindowToken appWindowToken = mChildren.get(i); appWindowToken.writeToProto(proto, APP_WINDOW_TOKENS, trim); } - proto.write(FILLS_PARENT, mFillsParent); - mBounds.writeToProto(proto, BOUNDS); + proto.write(FILLS_PARENT, matchParentBounds()); + getBounds().writeToProto(proto, BOUNDS); mTempInsetBounds.writeToProto(proto, TEMP_INSET_BOUNDS); proto.end(token); } @@ -721,8 +684,7 @@ class Task extends WindowContainer<AppWindowToken> { final String doublePrefix = prefix + " "; pw.println(prefix + "taskId=" + mTaskId); - pw.println(doublePrefix + "mFillsParent=" + mFillsParent); - pw.println(doublePrefix + "mBounds=" + mBounds.toShortString()); + pw.println(doublePrefix + "mBounds=" + getBounds().toShortString()); pw.println(doublePrefix + "mdr=" + mDeferRemoval); pw.println(doublePrefix + "appTokens=" + mChildren); pw.println(doublePrefix + "mTempInsetBounds=" + mTempInsetBounds.toShortString()); diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 5d4ba0992f16..87d0a4016634 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -382,6 +382,27 @@ class TaskPositioner { mStartOrientationWasLandscape = startBounds.width() >= startBounds.height(); mWindowOriginalBounds.set(startBounds); + // Notify the app that resizing has started, even though we haven't received any new + // bounds yet. This will guarantee that the app starts the backdrop renderer before + // configuration changes which could cause an activity restart. + if (mResizing) { + synchronized (mService.mWindowMap) { + notifyMoveLocked(startX, startY); + } + + // Perform the resize on the WMS handler thread when we don't have the WMS lock held + // to ensure that we don't deadlock WMS and AMS. Note that WindowPositionerEventReceiver + // callbacks are delivered on the same handler so this initial resize is always + // guaranteed to happen before subsequent drag resizes. + mService.mH.post(() -> { + try { + mService.mActivityManager.resizeTask( + mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED); + } catch (RemoteException e) { + } + }); + } + // Make sure we always have valid drag bounds even if the drag ends before any move events // have been handled. mWindowDragBounds.set(startBounds); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index e3c7515a0179..41915a32704d 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -18,9 +18,7 @@ package com.android.server.wm; import static android.graphics.Color.WHITE; import static android.graphics.Color.alpha; -import static android.view.SurfaceControl.HIDDEN; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; -import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE; diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 8a930efea678..4a3a3fc960a5 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -36,7 +36,6 @@ import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVID import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM; import static com.android.server.wm.proto.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING; import static com.android.server.wm.proto.StackProto.BOUNDS; import static com.android.server.wm.proto.StackProto.FILLS_PARENT; @@ -88,9 +87,6 @@ public class TaskStack extends WindowContainer<Task> implements private Rect mTmpRect2 = new Rect(); private Rect mTmpRect3 = new Rect(); - /** Content limits relative to the DisplayContent this sits in. */ - private Rect mBounds = new Rect(); - /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */ private final Rect mAdjustedBounds = new Rect(); @@ -100,9 +96,6 @@ public class TaskStack extends WindowContainer<Task> implements */ private final Rect mFullyAdjustedImeBounds = new Rect(); - /** Whether mBounds is fullscreen */ - private boolean mFillsParent = true; - // Device rotation as of the last time {@link #mBounds} was set. private int mRotation; @@ -180,27 +173,20 @@ public class TaskStack extends WindowContainer<Task> implements /** * Set the bounds of the stack and its containing tasks. * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen. - * @param configs Configuration for individual tasks, keyed by task id. * @param taskBounds Bounds for individual tasks, keyed by task id. + * @param taskTempInsetBounds Inset bounds for individual tasks, keyed by task id. * @return True if the stack bounds was changed. * */ boolean setBounds( - Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, - SparseArray<Rect> taskTempInsetBounds) { + Rect stackBounds, SparseArray<Rect> taskBounds, SparseArray<Rect> taskTempInsetBounds) { setBounds(stackBounds); // Update bounds of containing tasks. for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { final Task task = mChildren.get(taskNdx); - Configuration config = configs.get(task.mTaskId); - if (config != null) { - Rect bounds = taskBounds.get(task.mTaskId); - task.resizeLocked(bounds, config, false /* forced */); - task.setTempInsetBounds(taskTempInsetBounds != null ? - taskTempInsetBounds.get(task.mTaskId) : null); - } else { - Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?"); - } + task.setBounds(taskBounds.get(task.mTaskId), false /* forced */); + task.setTempInsetBounds(taskTempInsetBounds != null ? + taskTempInsetBounds.get(task.mTaskId) : null); } return true; } @@ -227,20 +213,20 @@ public class TaskStack extends WindowContainer<Task> implements final boolean adjusted = !mAdjustedBounds.isEmpty(); Rect insetBounds = null; if (adjusted && isAdjustedForMinimizedDockedStack()) { - insetBounds = mBounds; + insetBounds = getRawBounds(); } else if (adjusted && mAdjustedForIme) { if (mImeGoingAway) { - insetBounds = mBounds; + insetBounds = getRawBounds(); } else { insetBounds = mFullyAdjustedImeBounds; } } - alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds); + alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : getRawBounds(), insetBounds); mDisplayContent.setLayoutNeeded(); } private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) { - if (mFillsParent) { + if (matchParentBounds()) { return; } @@ -253,13 +239,15 @@ public class TaskStack extends WindowContainer<Task> implements } } - private void setAnimationBackgroundBounds(Rect bounds) { + private void updateAnimationBackgroundBounds() { if (mAnimationBackgroundSurface == null) { return; } + getRawBounds(mTmpRect); // TODO: Should be in relative coordinates. - getPendingTransaction().setSize(mAnimationBackgroundSurface, bounds.width(), bounds.height()) - .setPosition(mAnimationBackgroundSurface, bounds.left, bounds.top); + getPendingTransaction().setSize(mAnimationBackgroundSurface, mTmpRect.width(), + mTmpRect.height()).setPosition(mAnimationBackgroundSurface, mTmpRect.left, + mTmpRect.top); scheduleAnimation(); } @@ -283,50 +271,53 @@ public class TaskStack extends WindowContainer<Task> implements scheduleAnimation(); } - private boolean setBounds(Rect bounds) { - boolean oldFullscreen = mFillsParent; + @Override + public int setBounds(Rect bounds) { + return setBounds(getOverrideBounds(), bounds); + } + + private int setBounds(Rect existing, Rect bounds) { int rotation = Surface.ROTATION_0; int density = DENSITY_DPI_UNDEFINED; if (mDisplayContent != null) { - mDisplayContent.getLogicalDisplayRect(mTmpRect); + mDisplayContent.getBounds(mTmpRect); rotation = mDisplayContent.getDisplayInfo().rotation; density = mDisplayContent.getDisplayInfo().logicalDensityDpi; - mFillsParent = bounds == null; - if (mFillsParent) { - bounds = mTmpRect; - } } - if (bounds == null) { - // Can't set to fullscreen if we don't have a display to get bounds from... - return false; - } - if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) { - return false; + if (equivalentBounds(existing, bounds) && mRotation == rotation) { + return BOUNDS_CHANGE_NONE; } - setAnimationBackgroundBounds(bounds); + final int result = super.setBounds(bounds); + + if (mDisplayContent != null) { + updateAnimationBackgroundBounds(); + } - mBounds.set(bounds); mRotation = rotation; mDensity = density; updateAdjustedBounds(); - return true; + return result; } /** Bounds of the stack without adjusting for other factors in the system like visibility * of docked stack. - * Most callers should be using {@link #getBounds} as it take into consideration other system - * factors. */ + * Most callers should be using {@link ConfigurationContainer#getOverrideBounds} as it take into + * consideration other system factors. */ void getRawBounds(Rect out) { - out.set(mBounds); + out.set(getRawBounds()); + } + + Rect getRawBounds() { + return super.getBounds(); } /** Return true if the current bound can get outputted to the rest of the system as-is. */ private boolean useCurrentBounds() { - if (mFillsParent + if (matchParentBounds() || !inSplitScreenSecondaryWindowingMode() || mDisplayContent == null || mDisplayContent.getSplitScreenPrimaryStack() != null) { @@ -335,24 +326,29 @@ public class TaskStack extends WindowContainer<Task> implements return false; } - public void getBounds(Rect out) { + @Override + public void getBounds(Rect bounds) { + bounds.set(getBounds()); + } + + @Override + public Rect getBounds() { if (useCurrentBounds()) { // If we're currently adjusting for IME or minimized docked stack, we use the adjusted // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked // stack is visible since it is already what we want to represent to the rest of the // system. if (!mAdjustedBounds.isEmpty()) { - out.set(mAdjustedBounds); + return mAdjustedBounds; } else { - out.set(mBounds); + return super.getBounds(); } - return; } // The bounds has been adjusted to accommodate for a docked stack, but the docked stack // is not currently visible. Go ahead a represent it as fullscreen to the rest of the // system. - mDisplayContent.getLogicalDisplayRect(out); + return mDisplayContent.getBounds(); } /** @@ -373,7 +369,7 @@ public class TaskStack extends WindowContainer<Task> implements mBoundsAnimationSourceHintBounds.setEmpty(); } - mPreAnimationBounds.set(mBounds); + mPreAnimationBounds.set(getRawBounds()); } /** @@ -418,12 +414,12 @@ public class TaskStack extends WindowContainer<Task> implements if (bounds != null) { setBounds(bounds); return; - } else if (mFillsParent) { + } else if (matchParentBounds()) { setBounds(null); return; } - mTmpRect2.set(mBounds); + mTmpRect2.set(getRawBounds()); final int newRotation = mDisplayContent.getDisplayInfo().rotation; final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi; if (mRotation == newRotation && mDensity == newDensity) { @@ -466,14 +462,14 @@ public class TaskStack extends WindowContainer<Task> implements return false; } - if (mFillsParent) { + if (matchParentBounds()) { // Update stack bounds again since rotation changed since updateDisplayInfo(). setBounds(null); // Return false since we don't need the client to resize. return false; } - mTmpRect2.set(mBounds); + mTmpRect2.set(getRawBounds()); mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2); if (inSplitScreenPrimaryWindowingMode()) { repositionPrimarySplitScreenStackAfterRotation(mTmpRect2); @@ -510,7 +506,7 @@ public class TaskStack extends WindowContainer<Task> implements if (mDisplayContent.getDockedDividerController().canPrimaryStackDockTo(dockSide)) { return; } - mDisplayContent.getLogicalDisplayRect(mTmpRect); + mDisplayContent.getBounds(mTmpRect); dockSide = DockedDividerUtils.invertDockSide(dockSide); switch (dockSide) { case DOCKED_LEFT: @@ -755,7 +751,7 @@ public class TaskStack extends WindowContainer<Task> implements // not fullscreen. If it's fullscreen, it means that we are in the transition of // dismissing it, so we must not resize this stack. bounds = new Rect(); - mDisplayContent.getLogicalDisplayRect(mTmpRect); + mDisplayContent.getBounds(mTmpRect); mTmpRect2.setEmpty(); if (splitScreenStack != null) { splitScreenStack.getRawBounds(mTmpRect2); @@ -818,7 +814,7 @@ public class TaskStack extends WindowContainer<Task> implements } if (!inSplitScreenWindowingMode() || mDisplayContent == null) { - outStackBounds.set(mBounds); + outStackBounds.set(getRawBounds()); return; } @@ -833,7 +829,7 @@ public class TaskStack extends WindowContainer<Task> implements // The docked stack is being dismissed, but we caught before it finished being // dismissed. In that case we want to treat it as if it is not occupying any space and // let others occupy the whole display. - mDisplayContent.getLogicalDisplayRect(outStackBounds); + mDisplayContent.getBounds(outStackBounds); return; } @@ -841,11 +837,11 @@ public class TaskStack extends WindowContainer<Task> implements if (dockedSide == DOCKED_INVALID) { // Not sure how you got here...Only thing we can do is return current bounds. Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack); - outStackBounds.set(mBounds); + outStackBounds.set(getRawBounds()); return; } - mDisplayContent.getLogicalDisplayRect(mTmpRect); + mDisplayContent.getBounds(mTmpRect); dockedStack.getRawBounds(mTmpRect2); final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; getStackDockedModeBounds(mTmpRect, outStackBounds, mTmpRect2, @@ -1146,14 +1142,14 @@ public class TaskStack extends WindowContainer<Task> implements // occluded by IME. We shift its bottom up by the height of the IME, but // leaves at least 30% of the top stack visible. final int minTopStackBottom = - getMinTopStackBottom(displayContentRect, mBounds.bottom); + getMinTopStackBottom(displayContentRect, getRawBounds().bottom); final int bottom = Math.max( - mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive, + getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive, minTopStackBottom); - mTmpAdjustedBounds.set(mBounds); - mTmpAdjustedBounds.bottom = - (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom); - mFullyAdjustedImeBounds.set(mBounds); + mTmpAdjustedBounds.set(getRawBounds()); + mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) + * getRawBounds().bottom); + mFullyAdjustedImeBounds.set(getRawBounds()); } else { // When the stack is on bottom and has no focus, it's only adjusted for divider width. final int dividerWidthDelta = dividerWidthInactive - dividerWidth; @@ -1163,22 +1159,24 @@ public class TaskStack extends WindowContainer<Task> implements // We try to move it up by the height of the IME window, but only to the extent // that leaves at least 30% of the top stack visible. // 'top' is where the top of bottom stack will move to in this case. - final int topBeforeImeAdjust = mBounds.top - dividerWidth + dividerWidthInactive; + final int topBeforeImeAdjust = + getRawBounds().top - dividerWidth + dividerWidthInactive; final int minTopStackBottom = - getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth); + getMinTopStackBottom(displayContentRect, + getRawBounds().top - dividerWidth); final int top = Math.max( - mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive); + getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive); - mTmpAdjustedBounds.set(mBounds); + mTmpAdjustedBounds.set(getRawBounds()); // Account for the adjustment for IME and divider width separately. // (top - topBeforeImeAdjust) is the amount of movement due to IME only, // and dividerWidthDelta is due to divider width change only. - mTmpAdjustedBounds.top = mBounds.top + + mTmpAdjustedBounds.top = getRawBounds().top + (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) + mAdjustDividerAmount * dividerWidthDelta); - mFullyAdjustedImeBounds.set(mBounds); + mFullyAdjustedImeBounds.set(getRawBounds()); mFullyAdjustedImeBounds.top = top; - mFullyAdjustedImeBounds.bottom = top + mBounds.height(); + mFullyAdjustedImeBounds.bottom = top + getRawBounds().height(); } return true; } @@ -1192,21 +1190,21 @@ public class TaskStack extends WindowContainer<Task> implements if (dockSide == DOCKED_TOP) { mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); int topInset = mTmpRect.top; - mTmpAdjustedBounds.set(mBounds); - mTmpAdjustedBounds.bottom = - (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom); + mTmpAdjustedBounds.set(getRawBounds()); + mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount) + * getRawBounds().bottom); } else if (dockSide == DOCKED_LEFT) { - mTmpAdjustedBounds.set(mBounds); - final int width = mBounds.width(); + mTmpAdjustedBounds.set(getRawBounds()); + final int width = getRawBounds().width(); mTmpAdjustedBounds.right = (int) (minimizeAmount * mDockedStackMinimizeThickness - + (1 - minimizeAmount) * mBounds.right); + + (1 - minimizeAmount) * getRawBounds().right); mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width; } else if (dockSide == DOCKED_RIGHT) { - mTmpAdjustedBounds.set(mBounds); - mTmpAdjustedBounds.left = - (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness) - + (1 - minimizeAmount) * mBounds.left); + mTmpAdjustedBounds.set(getRawBounds()); + mTmpAdjustedBounds.left = (int) (minimizeAmount * + (getRawBounds().right - mDockedStackMinimizeThickness) + + (1 - minimizeAmount) * getRawBounds().left); } return true; } @@ -1228,9 +1226,9 @@ public class TaskStack extends WindowContainer<Task> implements if (dockSide == DOCKED_TOP) { mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); int topInset = mTmpRect.top; - return mBounds.bottom - topInset; + return getRawBounds().bottom - topInset; } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) { - return mBounds.width() - mDockedStackMinimizeThickness; + return getRawBounds().width() - mDockedStackMinimizeThickness; } else { return 0; } @@ -1264,11 +1262,12 @@ public class TaskStack extends WindowContainer<Task> implements return; } - final Rect insetBounds = mImeGoingAway ? mBounds : mFullyAdjustedImeBounds; + final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds; task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP); mDisplayContent.setLayoutNeeded(); } + boolean isAdjustedForMinimizedDockedStack() { return mMinimizeAmount != 0f; } @@ -1282,8 +1281,8 @@ public class TaskStack extends WindowContainer<Task> implements for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) { mChildren.get(taskNdx).writeToProto(proto, TASKS, trim); } - proto.write(FILLS_PARENT, mFillsParent); - mBounds.writeToProto(proto, BOUNDS); + proto.write(FILLS_PARENT, matchParentBounds()); + getRawBounds().writeToProto(proto, BOUNDS); proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown); proto.end(token); } @@ -1291,8 +1290,7 @@ public class TaskStack extends WindowContainer<Task> implements public void dump(String prefix, PrintWriter pw) { pw.println(prefix + "mStackId=" + mStackId); pw.println(prefix + "mDeferRemoval=" + mDeferRemoval); - pw.println(prefix + "mFillsParent=" + mFillsParent); - pw.println(prefix + "mBounds=" + mBounds.toShortString()); + pw.println(prefix + "mBounds=" + getRawBounds().toShortString()); if (mMinimizeAmount != 0f) { pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount); } @@ -1323,18 +1321,10 @@ public class TaskStack extends WindowContainer<Task> implements } } - /** Fullscreen status of the stack without adjusting for other factors in the system like - * visibility of docked stack. - * Most callers should be using {@link #fillsParent} as it take into consideration other - * system factors. */ - boolean getRawFullscreen() { - return mFillsParent; - } - @Override boolean fillsParent() { if (useCurrentBounds()) { - return mFillsParent; + return matchParentBounds(); } // The bounds has been adjusted to accommodate for a docked stack, but the docked stack // is not currently visible. Go ahead a represent it as fullscreen to the rest of the @@ -1360,7 +1350,7 @@ public class TaskStack extends WindowContainer<Task> implements * information which side of the screen was the dock anchored. */ int getDockSide() { - return getDockSide(mBounds); + return getDockSide(getRawBounds()); } private int getDockSide(Rect bounds) { @@ -1370,7 +1360,7 @@ public class TaskStack extends WindowContainer<Task> implements if (mDisplayContent == null) { return DOCKED_INVALID; } - mDisplayContent.getLogicalDisplayRect(mTmpRect); + mDisplayContent.getBounds(mTmpRect); final int orientation = mDisplayContent.getConfiguration().orientation; return getDockSideUnchecked(bounds, mTmpRect, orientation); } @@ -1490,7 +1480,7 @@ public class TaskStack extends WindowContainer<Task> implements */ if (task.isActivityTypeHome() && isMinimizedDockAndHomeStackResizable()) { - mDisplayContent.getLogicalDisplayRect(mTmpRect); + mDisplayContent.getBounds(mTmpRect); } else { task.getDimBounds(mTmpRect); } diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java index b3bb0b7e001d..5caae32dbeb4 100644 --- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java +++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java @@ -18,7 +18,6 @@ package com.android.server.wm; import android.app.ActivityManager.TaskDescription; import android.app.ActivityManager.TaskSnapshot; -import android.content.res.Configuration; import android.graphics.Rect; import android.os.Handler; import android.os.Looper; @@ -30,6 +29,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.lang.ref.WeakReference; import static com.android.server.EventLogTags.WM_TASK_CREATED; +import static com.android.server.wm.ConfigurationContainer.BOUNDS_CHANGE_NONE; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -75,7 +75,7 @@ public class TaskWindowContainerController + stackController); } EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId); - final Task task = createTask(taskId, stack, userId, bounds, resizeMode, + final Task task = createTask(taskId, stack, userId, resizeMode, supportsPictureInPicture, taskDescription); final int position = toTop ? POSITION_TOP : POSITION_BOTTOM; // We only want to move the parents to the parents if we are creating this task at the @@ -85,10 +85,10 @@ public class TaskWindowContainerController } @VisibleForTesting - Task createTask(int taskId, TaskStack stack, int userId, Rect bounds, int resizeMode, + Task createTask(int taskId, TaskStack stack, int userId, int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription) { - return new Task(taskId, stack, userId, mService, bounds, resizeMode, - supportsPictureInPicture, taskDescription, this); + return new Task(taskId, stack, userId, mService, resizeMode, supportsPictureInPicture, + taskDescription, this); } @Override @@ -151,14 +151,14 @@ public class TaskWindowContainerController } } - public void resize(Rect bounds, Configuration overrideConfig, boolean relayout, - boolean forced) { + public void resize(boolean relayout, boolean forced) { synchronized (mWindowMap) { if (mContainer == null) { throw new IllegalArgumentException("resizeTask: taskId " + mTaskId + " not found."); } - if (mContainer.resizeLocked(bounds, overrideConfig, forced) && relayout) { + if (mContainer.setBounds(mContainer.getOverrideBounds(), forced) != BOUNDS_CHANGE_NONE + && relayout) { mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a5e62884a9fb..64675825243e 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -318,11 +318,24 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * @see #mFullConfiguration */ @Override - final public void onOverrideConfigurationChanged(Configuration overrideConfiguration) { + public void onOverrideConfigurationChanged(Configuration overrideConfiguration) { + // We must diff before the configuration is applied so that we can capture the change + // against the existing bounds. + final int diff = diffOverrideBounds(overrideConfiguration.windowConfiguration.getBounds()); super.onOverrideConfigurationChanged(overrideConfiguration); if (mParent != null) { mParent.onDescendantOverrideConfigurationChanged(); } + + if (diff == BOUNDS_CHANGE_NONE) { + return; + } + + if ((diff & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) { + onResize(); + } else { + onMovedByResize(); + } } /** @@ -718,29 +731,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< final WindowContainer p = getParent(); // Give the parent a chance to set properties. In hierarchy v1 we rely // on this to set full-screen dimensions on all our Surface-less Layers. - final SurfaceControl.Builder b = p.makeChildSurface(child); - if (child != null && child.isScreenOverlay()) { - // If it's a screen overlay it's been promoted in the hierarchy (wrt to the - // WindowContainer hierarchy vs the SurfaceControl hierarchy) - // and we shouldn't set ourselves as the parent. - return b; - } else { - return b.setParent(mSurfaceControl); - } - } - - /** - * There are various layers which require promotion from the WindowContainer - * hierarchy to the Overlay layer described in {@link DisplayContent}. See {@link WindowState} - * for the particular usage. - * - * TODO: Perhaps this should be eliminated, either through modifying - * the window container hierarchy or through modifying the way we express these overlay - * Surfaces (for example, the Magnification Overlay could be implemented like the Strict-mode - * Flash and not actually use a WindowState). - */ - boolean isScreenOverlay() { - return false; + return p.makeChildSurface(child) + .setParent(mSurfaceControl); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index d88cf4dad14f..1cd40806619f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -126,7 +126,6 @@ import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.IAssistDataReceiver; import android.content.BroadcastReceiver; -import android.content.ClipData; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -5517,7 +5516,7 @@ public class WindowManagerService extends IWindowManager.Stub } void reconfigureDisplayLocked(@NonNull DisplayContent displayContent) { - if (!mDisplayReady) { + if (!displayContent.isReady()) { return; } displayContent.configureDisplayPolicy(); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e53fccf3e943..502fc127e0cf 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -58,6 +58,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; @@ -814,7 +815,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP layoutXDiff = 0; layoutYDiff = 0; } else { - getContainerBounds(mContainingFrame); + getBounds(mContainingFrame); if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) { // If the bounds are frozen, we still want to translate the window freely and only @@ -972,7 +973,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mContentInsets.setEmpty(); mVisibleInsets.setEmpty(); } else { - getDisplayContent().getLogicalDisplayRect(mTmpRect); + getDisplayContent().getBounds(mTmpRect); // Override right and/or bottom insets in case if the frame doesn't fit the screen in // non-fullscreen mode. boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer @@ -1044,6 +1045,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " of=" + mOutsets.toShortString()); } + // TODO: Look into whether this override is still necessary. + @Override + public Rect getBounds() { + if (isInMultiWindowMode()) { + return getTask().getBounds(); + } else if (mAppToken != null){ + return mAppToken.getBounds(); + } else { + return super.getBounds(); + } + } + @Override public Rect getFrameLw() { return mFrame; @@ -2951,33 +2964,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** Is this window in a container that takes up the entire screen space? */ private boolean inFullscreenContainer() { - if (mAppToken == null) { - return true; - } - if (mAppToken.hasBounds()) { - return false; - } - return !isInMultiWindowMode(); + return mAppToken == null || (mAppToken.matchParentBounds() && !isInMultiWindowMode()); } /** @return true when the window is in fullscreen task, but has non-fullscreen bounds set. */ boolean isLetterboxedAppWindow() { - final Task task = getTask(); - final boolean taskIsFullscreen = task != null && task.isFullscreen(); - final boolean appWindowIsFullscreen = mAppToken != null && !mAppToken.hasBounds(); - - return taskIsFullscreen && !appWindowIsFullscreen; - } - - /** Returns the appropriate bounds to use for computing frames. */ - private void getContainerBounds(Rect outBounds) { - if (isInMultiWindowMode()) { - getTask().getBounds(outBounds); - } else if (mAppToken != null){ - mAppToken.getBounds(outBounds); - } else { - outBounds.setEmpty(); - } + return !isInMultiWindowMode() && mAppToken != null && !mAppToken.matchParentBounds(); } boolean isDragResizeChanged() { @@ -4355,24 +4347,16 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean shouldMagnify() { if (mAttrs.type == TYPE_INPUT_METHOD || - mAttrs.type == TYPE_INPUT_METHOD_DIALOG) { - return false; - } else if (isScreenOverlay()) { - return false; - } - return true; - } - - @Override - boolean isScreenOverlay() { - // It's tempting to wonder: Have we forgotten the rounded corners overlay? - // worry not: it's a fake TYPE_NAVIGATION_BAR. - if (mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || + mAttrs.type == TYPE_INPUT_METHOD_DIALOG || + mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || mAttrs.type == TYPE_NAVIGATION_BAR || + // It's tempting to wonder: Have we forgotten the rounded corners overlay? + // worry not: it's a fake TYPE_NAVIGATION_BAR_PANEL + mAttrs.type == TYPE_NAVIGATION_BAR_PANEL || mAttrs.type == TYPE_STATUS_BAR) { - return true; + return false; } - return false; + return true; } @Override diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index d11b0dbcc130..d2b37481a429 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1971,7 +1971,7 @@ class WindowStateAnimator { final float width = w.mFrame.width(); final float height = w.mFrame.height(); - mService.getDefaultDisplayContentLocked().getLogicalDisplayRect(displayRect); + mService.getDefaultDisplayContentLocked().getBounds(displayRect); final float displayWidth = displayRect.width(); final float displayHeight = displayRect.height(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java index 608635491849..4a6bee5c7365 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java @@ -64,6 +64,8 @@ final class NetworkLoggingHandler extends Handler { private final DevicePolicyManagerService mDpm; private final AlarmManager mAlarmManager; + private long mId; + private final OnAlarmListener mBatchTimeoutAlarmListener = new OnAlarmListener() { @Override public void onAlarm() { @@ -185,6 +187,10 @@ final class NetworkLoggingHandler extends Handler { private Bundle finalizeBatchAndBuildDeviceOwnerMessageLocked() { Bundle notificationExtras = null; if (mNetworkEvents.size() > 0) { + // Assign ids to the events. + for (NetworkEvent event : mNetworkEvents) { + event.setId(mId++); + } // Finalize the batch and start a new one from scratch. if (mBatches.size() >= MAX_BATCHES) { // Remove the oldest batch if we hit the limit. diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index d90e284d44a0..ee45595f9a87 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -37,7 +37,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.ComponentName; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.filters.MediumTest; diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java index bc503c49a99a..b4919b6f9c52 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java @@ -28,7 +28,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import android.app.ActivityOptions; import android.app.IApplicationThread; -import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -108,8 +107,8 @@ public class ActivityStarterTests extends ActivityTestsBase { final Rect bounds = new Rect(10, 10, 100, 100); mStarter.updateBounds(task, bounds); - assertEquals(task.mBounds, bounds); - assertEquals(task.getStack().mBounds, null); + assertEquals(task.getOverrideBounds(), bounds); + assertEquals(new Rect(), task.getStack().getOverrideBounds()); // When in a resizeable stack, the stack bounds should be updated as well. final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor) @@ -124,10 +123,10 @@ public class ActivityStarterTests extends ActivityTestsBase { // In the case of no animation, the stack and task bounds should be set immediately. if (!ANIMATE) { - assertEquals(task2.getStack().mBounds, bounds); - assertEquals(task2.mBounds, bounds); + assertEquals(task2.getStack().getOverrideBounds(), bounds); + assertEquals(task2.getOverrideBounds(), bounds); } else { - assertEquals(task2.mBounds, null); + assertEquals(task2.getOverrideBounds(), new Rect()); } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index 9683e229d50f..2fffb892c5f0 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -19,6 +19,7 @@ package com.android.server.am; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.view.Display.DEFAULT_DISPLAY; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.doNothing; @@ -415,6 +416,12 @@ public class ActivityTestsBase { @Override protected T createStackWindowController(int displayId, boolean onTop, Rect outBounds) { mContainerController = (T) WindowTestUtils.createMockStackWindowContainerController(); + + // Primary pinned stacks require a non-empty out bounds to be set or else all tasks + // will be moved to the full screen stack. + if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + outBounds.set(0, 0, 100, 100); + } return mContainerController; } diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java index 01e2da61f0d4..13daf3e94508 100644 --- a/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java +++ b/services/tests/servicestests/src/com/android/server/am/LaunchingTaskPositionerTests.java @@ -16,7 +16,6 @@ package com.android.server.am; -import android.content.ComponentName; import android.content.pm.ActivityInfo.WindowLayout; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -93,8 +92,8 @@ public class LaunchingTaskPositionerTests extends ActivityTestsBase { */ @Test public void testInitialBounds() throws Exception { - assertEquals(mStack.mBounds, STACK_BOUNDS); - assertEquals(mTask.mBounds, null); + assertEquals(mStack.getOverrideBounds(), STACK_BOUNDS); + assertEquals(mTask.getOverrideBounds(), new Rect()); } /** @@ -182,7 +181,7 @@ public class LaunchingTaskPositionerTests extends ActivityTestsBase { mService.mStackSupervisor.getLaunchingBoundsController().layoutTask(mTask, layout); // Second task will be laid out on top of the first so starting bounds is the same. - final Rect expectedBounds = new Rect(mTask.mBounds); + final Rect expectedBounds = new Rect(mTask.getOverrideBounds()); ActivityRecord activity = null; TaskRecord secondTask = null; @@ -203,14 +202,16 @@ public class LaunchingTaskPositionerTests extends ActivityTestsBase { if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT) || (gravity & (Gravity.BOTTOM | Gravity.RIGHT)) == (Gravity.BOTTOM | Gravity.RIGHT)) { - expectedBounds.offset(-LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds), - 0); + expectedBounds.offset(-LaunchingTaskPositioner.getHorizontalStep( + mStack.getOverrideBounds()), 0); } else if ((gravity & Gravity.TOP) == Gravity.TOP || (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { - expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds), 0); + expectedBounds.offset( + LaunchingTaskPositioner.getHorizontalStep(mStack.getOverrideBounds()), 0); } else { - expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds), - LaunchingTaskPositioner.getVerticalStep(mStack.mBounds)); + expectedBounds.offset( + LaunchingTaskPositioner.getHorizontalStep(mStack.getOverrideBounds()), + LaunchingTaskPositioner.getVerticalStep(mStack.getOverrideBounds())); } assertEquals(mResult, expectedBounds); @@ -228,10 +229,12 @@ public class LaunchingTaskPositionerTests extends ActivityTestsBase { private Rect getDefaultBounds(int gravity) { final Rect bounds = new Rect(); - bounds.set(mStack.mBounds); + bounds.set(mStack.getOverrideBounds()); - final int verticalInset = LaunchingTaskPositioner.getFreeformStartTop(mStack.mBounds); - final int horizontalInset = LaunchingTaskPositioner.getFreeformStartLeft(mStack.mBounds); + final int verticalInset = + LaunchingTaskPositioner.getFreeformStartTop(mStack.getOverrideBounds()); + final int horizontalInset = + LaunchingTaskPositioner.getFreeformStartLeft(mStack.getOverrideBounds()); bounds.inset(horizontalInset, verticalInset); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java index db317a0e4cca..a92d1db2616b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java @@ -20,8 +20,6 @@ import android.app.admin.DnsEvent; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; -import static junit.framework.Assert.assertEquals; - @SmallTest public class NetworkEventTest extends DpmTestBase { @@ -30,6 +28,7 @@ public class NetworkEventTest extends DpmTestBase { */ public void testConnectEventParceling() { ConnectEvent event = new ConnectEvent("127.0.0.1", 80, "com.android.whateverdude", 100000); + event.setId(5L); Parcel p = Parcel.obtain(); p.writeParcelable(event, 0); p.setDataPosition(0); @@ -39,6 +38,7 @@ public class NetworkEventTest extends DpmTestBase { assertEquals(event.getPort(), unparceledEvent.getPort()); assertEquals(event.getPackageName(), unparceledEvent.getPackageName()); assertEquals(event.getTimestamp(), unparceledEvent.getTimestamp()); + assertEquals(event.getId(), unparceledEvent.getId()); } /** @@ -47,6 +47,7 @@ public class NetworkEventTest extends DpmTestBase { public void testDnsEventParceling() { DnsEvent event = new DnsEvent("d.android.com", new String[]{"192.168.0.1", "127.0.0.1"}, 2, "com.android.whateverdude", 100000); + event.setId(5L); Parcel p = Parcel.obtain(); p.writeParcelable(event, 0); p.setDataPosition(0); @@ -59,5 +60,6 @@ public class NetworkEventTest extends DpmTestBase { unparceledEvent.getTotalResolvedAddressCount()); assertEquals(event.getPackageName(), unparceledEvent.getPackageName()); assertEquals(event.getTimestamp(), unparceledEvent.getTimestamp()); + assertEquals(event.getId(), unparceledEvent.getId()); } } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index 5d2bb4d92620..503e1ac4563d 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -21,7 +21,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import android.app.ActivityManager.TaskDescription; -import android.content.Context; +import android.content.res.Configuration; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.InstrumentationRegistry; @@ -75,11 +75,23 @@ public class WindowFrameTests extends WindowTestsBase { final Rect mInsetBounds = new Rect(); boolean mFullscreenForTest = true; TaskWithBounds(Rect bounds) { - super(0, mStubStack, 0, sWm, null, 0, false, new TaskDescription(), null); + super(0, mStubStack, 0, sWm, 0, false, new TaskDescription(), null); mBounds = bounds; + setBounds(bounds); } + + @Override + public Rect getBounds() { + return mBounds; + } + + @Override + public void getBounds(Rect out) { + out.set(mBounds); + } + @Override - void getBounds(Rect outBounds) { + public void getOverrideBounds(Rect outBounds) { outBounds.set(mBounds); } @Override @@ -395,7 +407,9 @@ public class WindowFrameTests extends WindowTestsBase { final int xInset = logicalWidth / 10; final int yInset = logicalWidth / 10; final Rect cf = new Rect(xInset, yInset, logicalWidth - xInset, logicalHeight - yInset); - w.mAppToken.onOverrideConfigurationChanged(w.mAppToken.getOverrideConfiguration(), cf); + Configuration config = new Configuration(w.mAppToken.getOverrideConfiguration()); + config.windowConfiguration.setBounds(cf); + w.mAppToken.onOverrideConfigurationChanged(config); pf.set(0, 0, logicalWidth, logicalHeight); task.mFullscreenForTest = true; diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java index 1aafac66bae4..b2334e848d4d 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java @@ -29,11 +29,17 @@ import android.view.WindowManager; import static android.app.AppOpsManager.OP_NONE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.res.Configuration.EMPTY; + import static com.android.server.wm.WindowContainer.POSITION_TOP; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyFloat; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.mockito.invocation.InvocationOnMock; + /** * A collection of static functions that can be referenced by other test packages to provide access * to WindowManager related test functionality. @@ -64,13 +70,24 @@ public class WindowTestUtils { public static StackWindowController createMockStackWindowContainerController() { StackWindowController controller = mock(StackWindowController.class); controller.mContainer = mock(TestTaskStack.class); + + // many components rely on the {@link StackWindowController#adjustConfigurationForBounds} + // to properly set bounds values in the configuration. We must mimick those actions here. + doAnswer((InvocationOnMock invocationOnMock) -> { + final Configuration config = invocationOnMock.<Configuration>getArgument(7); + final Rect bounds = invocationOnMock.<Rect>getArgument(0); + config.windowConfiguration.setBounds(bounds); + return null; + }).when(controller).adjustConfigurationForBounds(any(), any(), any(), any(), + anyBoolean(), anyBoolean(), anyFloat(), any(), any()); + return controller; } /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ public static Task createTaskInStack(WindowManagerService service, TaskStack stack, int userId) { - final Task newTask = new Task(sNextTaskId++, stack, userId, service, null, 0, false, + final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false, new ActivityManager.TaskDescription(), null); stack.addTask(newTask, POSITION_TOP); return newTask; @@ -98,17 +115,17 @@ public class WindowTestUtils { TestAppWindowToken(DisplayContent dc) { super(dc.mService, new IApplicationToken.Stub() { public String getName() {return null;} - }, false, dc, true /* fillsParent */, null /* bounds */); + }, false, dc, true /* fillsParent */); } TestAppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) { + boolean alwaysFocusable, AppWindowContainerController controller) { super(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, - launchTaskBehind, alwaysFocusable, controller, bounds); + launchTaskBehind, alwaysFocusable, controller); } int getWindowsCount() { @@ -175,10 +192,10 @@ public class WindowTestUtils { private boolean mUseLocalIsAnimating = false; private boolean mIsAnimating = false; - TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds, + TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode, boolean supportsPictureInPicture, TaskWindowContainerController controller) { - super(taskId, stack, userId, service, bounds, resizeMode, supportsPictureInPicture, + super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture, new ActivityManager.TaskDescription(), controller); } @@ -247,9 +264,9 @@ public class WindowTestUtils { } @Override - TestTask createTask(int taskId, TaskStack stack, int userId, Rect bounds, int resizeMode, + TestTask createTask(int taskId, TaskStack stack, int userId, int resizeMode, boolean supportsPictureInPicture, ActivityManager.TaskDescription taskDescription) { - return new TestTask(taskId, stack, userId, mService, bounds, resizeMode, + return new TestTask(taskId, stack, userId, mService, resizeMode, supportsPictureInPicture, this); } } @@ -269,8 +286,7 @@ public class WindowTestUtils { true /* showForAllUsers */, 0 /* configChanges */, false /* voiceInteraction */, false /* launchTaskBehind */, false /* alwaysFocusable */, 0 /* targetSdkVersion */, 0 /* rotationAnimationHint */, - 0 /* inputDispatchingTimeoutNanos */, taskController.mService, - null /* bounds */); + 0 /* inputDispatchingTimeoutNanos */, taskController.mService); mToken = token; } @@ -279,12 +295,12 @@ public class WindowTestUtils { boolean voiceInteraction, DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint, int configChanges, boolean launchTaskBehind, - boolean alwaysFocusable, AppWindowContainerController controller, Rect bounds) { + boolean alwaysFocusable, AppWindowContainerController controller) { return new TestAppWindowToken(service, token, voiceInteraction, dc, inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, orientation, rotationAnimationHint, configChanges, launchTaskBehind, alwaysFocusable, - controller, bounds); + controller); } AppWindowToken getAppWindowToken(DisplayContent dc) { diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java index f7c4b1f51c46..4e23b515eb4b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java @@ -185,7 +185,6 @@ public class ZOrderingTests extends WindowTestsBase { // target. assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove); assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow); - assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow); assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow); assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 44e5314ff7dd..2d93da9fa9fa 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -427,8 +427,11 @@ public class VoiceInteractionManagerService extends SystemService { if (hasComponent) { mShortcutServiceInternal.setShortcutHostPackage(TAG, serviceComponent.getPackageName(), mCurUser); + mAmInternal.setAllowAppSwitches(TAG, + serviceInfo.applicationInfo.uid, mCurUser); } else { mShortcutServiceInternal.setShortcutHostPackage(TAG, null, mCurUser); + mAmInternal.setAllowAppSwitches(TAG, -1, mCurUser); } } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 1db6ef7b10f0..69371a18d481 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -816,6 +816,14 @@ public class CarrierConfigManager { public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int"; /** + * Determines whether manage IMS conference calls is supported by a carrier. When {@code true}, + * manage IMS conference call is supported, {@code false otherwise}. + * @hide + */ + public static final String KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL = + "support_manage_ims_conference_call_bool"; + + /** * Determines whether High Definition audio property is displayed in the dialer UI. * If {@code false}, remove the HD audio property from the connection so that HD audio related * UI is not displayed. If {@code true}, keep HD audio property as it is configured. @@ -1814,6 +1822,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0); sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true); + sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false); sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false); sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5); diff --git a/telephony/java/android/telephony/data/DataProfile.aidl b/telephony/java/android/telephony/data/DataProfile.aidl new file mode 100644 index 000000000000..65fdf9164843 --- /dev/null +++ b/telephony/java/android/telephony/data/DataProfile.aidl @@ -0,0 +1,20 @@ +/* + * Copyright 2017 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. + */ + +/** @hide */ +package android.telephony.data; + +parcelable DataProfile; diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java new file mode 100644 index 000000000000..41c1430e386f --- /dev/null +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -0,0 +1,280 @@ +/* + * Copyright 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.data; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import com.android.internal.telephony.RILConstants; + +/** + * Description of a mobile data profile used for establishing + * data connections. + * + * @hide + */ +@SystemApi +public final class DataProfile implements Parcelable { + + // The types indicating the data profile is used on GSM (3GPP) or CDMA (3GPP2) network. + public static final int TYPE_COMMON = 0; + public static final int TYPE_3GPP = 1; + public static final int TYPE_3GPP2 = 2; + + private final int mProfileId; + + private final String mApn; + + private final String mProtocol; + + private final int mAuthType; + + private final String mUserName; + + private final String mPassword; + + private final int mType; + + private final int mMaxConnsTime; + + private final int mMaxConns; + + private final int mWaitTime; + + private final boolean mEnabled; + + private final int mSupportedApnTypesBitmap; + + private final String mRoamingProtocol; + + private final int mBearerBitmap; + + private final int mMtu; + + private final String mMvnoType; + + private final String mMvnoMatchData; + + private final boolean mModemCognitive; + + public DataProfile(int profileId, String apn, String protocol, int authType, + String userName, String password, int type, int maxConnsTime, int maxConns, + int waitTime, boolean enabled, int supportedApnTypesBitmap, String roamingProtocol, + int bearerBitmap, int mtu, String mvnoType, String mvnoMatchData, + boolean modemCognitive) { + + this.mProfileId = profileId; + this.mApn = apn; + this.mProtocol = protocol; + if (authType == -1) { + authType = TextUtils.isEmpty(userName) ? RILConstants.SETUP_DATA_AUTH_NONE + : RILConstants.SETUP_DATA_AUTH_PAP_CHAP; + } + this.mAuthType = authType; + this.mUserName = userName; + this.mPassword = password; + this.mType = type; + this.mMaxConnsTime = maxConnsTime; + this.mMaxConns = maxConns; + this.mWaitTime = waitTime; + this.mEnabled = enabled; + + this.mSupportedApnTypesBitmap = supportedApnTypesBitmap; + this.mRoamingProtocol = roamingProtocol; + this.mBearerBitmap = bearerBitmap; + this.mMtu = mtu; + this.mMvnoType = mvnoType; + this.mMvnoMatchData = mvnoMatchData; + this.mModemCognitive = modemCognitive; + } + + public DataProfile(Parcel source) { + mProfileId = source.readInt(); + mApn = source.readString(); + mProtocol = source.readString(); + mAuthType = source.readInt(); + mUserName = source.readString(); + mPassword = source.readString(); + mType = source.readInt(); + mMaxConnsTime = source.readInt(); + mMaxConns = source.readInt(); + mWaitTime = source.readInt(); + mEnabled = source.readBoolean(); + mSupportedApnTypesBitmap = source.readInt(); + mRoamingProtocol = source.readString(); + mBearerBitmap = source.readInt(); + mMtu = source.readInt(); + mMvnoType = source.readString(); + mMvnoMatchData = source.readString(); + mModemCognitive = source.readBoolean(); + } + + /** + * @return Id of the data profile. + */ + public int getProfileId() { return mProfileId; } + + /** + * @return The APN to establish data connection. + */ + public String getApn() { return mApn; } + + /** + * @return The connection protocol, should be one of the PDP_type values in TS 27.007 section + * 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP". + */ + public String getProtocol() { return mProtocol; } + + /** + * @return The authentication protocol used for this PDP context + * (None: 0, PAP: 1, CHAP: 2, PAP&CHAP: 3) + */ + public int getAuthType() { return mAuthType; } + + /** + * @return The username for APN. Can be null. + */ + public String getUserName() { return mUserName; } + + /** + * @return The password for APN. Can be null. + */ + public String getPassword() { return mPassword; } + + /** + * @return The profile type. Could be one of TYPE_COMMON, TYPE_3GPP, or TYPE_3GPP2. + */ + public int getType() { return mType; } + + /** + * @return The period in seconds to limit the maximum connections. + */ + public int getMaxConnsTime() { return mMaxConnsTime; } + + /** + * @return The maximum connections allowed. + */ + public int getMaxConns() { return mMaxConns; } + + /** + * @return The required wait time in seconds after a successful UE initiated disconnect of a + * given PDN connection before the device can send a new PDN connection request for that given + * PDN. + */ + public int getWaitTime() { return mWaitTime; } + + /** + * @return True if the profile is enabled. + */ + public boolean isEnabled() { return mEnabled; } + + /** + * @return The supported APN types bitmap. See RIL_ApnTypes for the value of each bit. + */ + public int getSupportedApnTypesBitmap() { return mSupportedApnTypesBitmap; } + + /** + * @return The connection protocol on roaming network, should be one of the PDP_type values in + * TS 27.007 section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP". + */ + public String getRoamingProtocol() { return mRoamingProtocol; } + + /** + * @return The bearer bitmap. See RIL_RadioAccessFamily for the value of each bit. + */ + public int getBearerBitmap() { return mBearerBitmap; } + + /** + * @return The maximum transmission unit (MTU) size in bytes. + */ + public int getMtu() { return mMtu; } + + /** + * @return The MVNO type: possible values are "imsi", "gid", "spn". + */ + public String getMvnoType() { return mMvnoType; } + + /** + * @return The MVNO match data. For example, + * SPN: A MOBILE, BEN NL, ... + * IMSI: 302720x94, 2060188, ... + * GID: 4E, 33, ... + */ + public String getMvnoMatchData() { return mMvnoMatchData; } + + /** + * @return True if the data profile was sent to the modem through setDataProfile earlier. + */ + public boolean isModemCognitive() { return mModemCognitive; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "DataProfile=" + mProfileId + "/" + mApn + "/" + mProtocol + "/" + mAuthType + + "/" + mUserName + "/" + mPassword + "/" + mType + "/" + mMaxConnsTime + + "/" + mMaxConns + "/" + mWaitTime + "/" + mEnabled + "/" + + mSupportedApnTypesBitmap + "/" + mRoamingProtocol + "/" + mBearerBitmap + "/" + + mMtu + "/" + mMvnoType + "/" + mMvnoMatchData + "/" + mModemCognitive; + } + + @Override + public boolean equals(Object o) { + if (o instanceof DataProfile == false) return false; + return (o == this || toString().equals(o.toString())); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mProfileId); + dest.writeString(mApn); + dest.writeString(mProtocol); + dest.writeInt(mAuthType); + dest.writeString(mUserName); + dest.writeString(mPassword); + dest.writeInt(mType); + dest.writeInt(mMaxConnsTime); + dest.writeInt(mMaxConns); + dest.writeInt(mWaitTime); + dest.writeBoolean(mEnabled); + dest.writeInt(mSupportedApnTypesBitmap); + dest.writeString(mRoamingProtocol); + dest.writeInt(mBearerBitmap); + dest.writeInt(mMtu); + dest.writeString(mMvnoType); + dest.writeString(mMvnoMatchData); + dest.writeBoolean(mModemCognitive); + } + + public static final Parcelable.Creator<DataProfile> CREATOR = + new Parcelable.Creator<DataProfile>() { + @Override + public DataProfile createFromParcel(Parcel source) { + return new DataProfile(source); + } + + @Override + public DataProfile[] newArray(int size) { + return new DataProfile[size]; + } + }; +} diff --git a/legacy-test/Android.mk b/test-base/Android.mk index 4c150c8f8a23..f73eff7d63aa 100644 --- a/legacy-test/Android.mk +++ b/test-base/Android.mk @@ -63,8 +63,8 @@ LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src LEGACY_TEST_OUTPUT_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/api.txt LEGACY_TEST_OUTPUT_REMOVED_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/legacy.test.stubs_intermediates/removed.txt -LEGACY_TEST_API_FILE := $(LOCAL_PATH)/api/legacy-test-current.txt -LEGACY_TEST_REMOVED_API_FILE := $(LOCAL_PATH)/api/legacy-test-removed.txt +LEGACY_TEST_API_FILE := $(LOCAL_PATH)/api/android-test-base-current.txt +LEGACY_TEST_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-base-removed.txt LOCAL_DROIDDOC_OPTIONS:= \ -stubpackages android.test:android.test.suitebuilder.annotation:com.android.internal.util:junit.framework \ @@ -119,7 +119,7 @@ $(eval $(call check-api, \ -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \ -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \ -error 25 -error 26 -error 27, \ - cat $(LOCAL_PATH)/api/apicheck_msg_legacy_test.txt, \ + cat $(LOCAL_PATH)/api/apicheck_msg_android_test_base.txt, \ check-legacy-test-api, \ $(call doc-timestamp-for,legacy-test-api-stubs-gen) \ )) diff --git a/legacy-test/api/legacy-test-current.txt b/test-base/api/android-test-base-current.txt index 7ebd6aa8a4a2..7ebd6aa8a4a2 100644 --- a/legacy-test/api/legacy-test-current.txt +++ b/test-base/api/android-test-base-current.txt diff --git a/legacy-test/api/legacy-test-removed.txt b/test-base/api/android-test-base-removed.txt index e69de29bb2d1..e69de29bb2d1 100644 --- a/legacy-test/api/legacy-test-removed.txt +++ b/test-base/api/android-test-base-removed.txt diff --git a/legacy-test/api/apicheck_msg_legacy_test.txt b/test-base/api/apicheck_msg_android_test_base.txt index ad5f2359b8b1..ad5f2359b8b1 100644 --- a/legacy-test/api/apicheck_msg_legacy_test.txt +++ b/test-base/api/apicheck_msg_android_test_base.txt diff --git a/legacy-test/jarjar-rules.txt b/test-base/jarjar-rules.txt index fd8555c8931c..fd8555c8931c 100644 --- a/legacy-test/jarjar-rules.txt +++ b/test-base/jarjar-rules.txt diff --git a/legacy-test/src/android/test/AndroidTestCase.java b/test-base/src/android/test/AndroidTestCase.java index 1e6bd9c14fd9..1e6bd9c14fd9 100644 --- a/legacy-test/src/android/test/AndroidTestCase.java +++ b/test-base/src/android/test/AndroidTestCase.java diff --git a/legacy-test/src/android/test/FlakyTest.java b/test-base/src/android/test/FlakyTest.java index 4e5c4e35a8c6..4e5c4e35a8c6 100644 --- a/legacy-test/src/android/test/FlakyTest.java +++ b/test-base/src/android/test/FlakyTest.java diff --git a/legacy-test/src/android/test/InstrumentationTestCase.java b/test-base/src/android/test/InstrumentationTestCase.java index 6b79314a4385..6b79314a4385 100644 --- a/legacy-test/src/android/test/InstrumentationTestCase.java +++ b/test-base/src/android/test/InstrumentationTestCase.java diff --git a/legacy-test/src/android/test/InstrumentationTestSuite.java b/test-base/src/android/test/InstrumentationTestSuite.java index a53fa267f1e1..a53fa267f1e1 100644 --- a/legacy-test/src/android/test/InstrumentationTestSuite.java +++ b/test-base/src/android/test/InstrumentationTestSuite.java diff --git a/legacy-test/src/android/test/PerformanceTestCase.java b/test-base/src/android/test/PerformanceTestCase.java index 65bd4a48f7f5..65bd4a48f7f5 100644 --- a/legacy-test/src/android/test/PerformanceTestCase.java +++ b/test-base/src/android/test/PerformanceTestCase.java diff --git a/legacy-test/src/android/test/RepetitiveTest.java b/test-base/src/android/test/RepetitiveTest.java index 6a7130e68e61..6a7130e68e61 100644 --- a/legacy-test/src/android/test/RepetitiveTest.java +++ b/test-base/src/android/test/RepetitiveTest.java diff --git a/legacy-test/src/android/test/UiThreadTest.java b/test-base/src/android/test/UiThreadTest.java index cd06ab890074..cd06ab890074 100644 --- a/legacy-test/src/android/test/UiThreadTest.java +++ b/test-base/src/android/test/UiThreadTest.java diff --git a/legacy-test/src/android/test/package.html b/test-base/src/android/test/package.html index 5be51359630e..5be51359630e 100644 --- a/legacy-test/src/android/test/package.html +++ b/test-base/src/android/test/package.html diff --git a/legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java b/test-base/src/android/test/suitebuilder/annotation/LargeTest.java index dc77ee6b2739..dc77ee6b2739 100644 --- a/legacy-test/src/android/test/suitebuilder/annotation/LargeTest.java +++ b/test-base/src/android/test/suitebuilder/annotation/LargeTest.java diff --git a/legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java b/test-base/src/android/test/suitebuilder/annotation/MediumTest.java index b941da03ac9a..b941da03ac9a 100644 --- a/legacy-test/src/android/test/suitebuilder/annotation/MediumTest.java +++ b/test-base/src/android/test/suitebuilder/annotation/MediumTest.java diff --git a/legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java b/test-base/src/android/test/suitebuilder/annotation/SmallTest.java index d3c74f019b53..d3c74f019b53 100644 --- a/legacy-test/src/android/test/suitebuilder/annotation/SmallTest.java +++ b/test-base/src/android/test/suitebuilder/annotation/SmallTest.java diff --git a/legacy-test/src/android/test/suitebuilder/annotation/Smoke.java b/test-base/src/android/test/suitebuilder/annotation/Smoke.java index aac293796be1..aac293796be1 100644 --- a/legacy-test/src/android/test/suitebuilder/annotation/Smoke.java +++ b/test-base/src/android/test/suitebuilder/annotation/Smoke.java diff --git a/legacy-test/src/android/test/suitebuilder/annotation/Suppress.java b/test-base/src/android/test/suitebuilder/annotation/Suppress.java index 629a3cf4a2cd..629a3cf4a2cd 100644 --- a/legacy-test/src/android/test/suitebuilder/annotation/Suppress.java +++ b/test-base/src/android/test/suitebuilder/annotation/Suppress.java diff --git a/legacy-test/src/android/test/suitebuilder/annotation/package.html b/test-base/src/android/test/suitebuilder/annotation/package.html index ffba2e9bf980..ffba2e9bf980 100644 --- a/legacy-test/src/android/test/suitebuilder/annotation/package.html +++ b/test-base/src/android/test/suitebuilder/annotation/package.html diff --git a/legacy-test/src/com/android/internal/util/Predicate.java b/test-base/src/com/android/internal/util/Predicate.java index e87f489f4670..e87f489f4670 100644 --- a/legacy-test/src/com/android/internal/util/Predicate.java +++ b/test-base/src/com/android/internal/util/Predicate.java diff --git a/legacy-test/src/junit/MODULE_LICENSE_CPL b/test-base/src/junit/MODULE_LICENSE_CPL index e69de29bb2d1..e69de29bb2d1 100644 --- a/legacy-test/src/junit/MODULE_LICENSE_CPL +++ b/test-base/src/junit/MODULE_LICENSE_CPL diff --git a/legacy-test/src/junit/README.android b/test-base/src/junit/README.android index 1384a1fedda2..1384a1fedda2 100644 --- a/legacy-test/src/junit/README.android +++ b/test-base/src/junit/README.android diff --git a/legacy-test/src/junit/cpl-v10.html b/test-base/src/junit/cpl-v10.html index 36aa208d4a29..36aa208d4a29 100644 --- a/legacy-test/src/junit/cpl-v10.html +++ b/test-base/src/junit/cpl-v10.html diff --git a/legacy-test/src/junit/framework/Assert.java b/test-base/src/junit/framework/Assert.java index 3dcc23d71c19..3dcc23d71c19 100644 --- a/legacy-test/src/junit/framework/Assert.java +++ b/test-base/src/junit/framework/Assert.java diff --git a/legacy-test/src/junit/framework/AssertionFailedError.java b/test-base/src/junit/framework/AssertionFailedError.java index 0d7802c431c6..0d7802c431c6 100644 --- a/legacy-test/src/junit/framework/AssertionFailedError.java +++ b/test-base/src/junit/framework/AssertionFailedError.java diff --git a/legacy-test/src/junit/framework/ComparisonCompactor.java b/test-base/src/junit/framework/ComparisonCompactor.java index e540f03b87d3..e540f03b87d3 100644 --- a/legacy-test/src/junit/framework/ComparisonCompactor.java +++ b/test-base/src/junit/framework/ComparisonCompactor.java diff --git a/legacy-test/src/junit/framework/ComparisonFailure.java b/test-base/src/junit/framework/ComparisonFailure.java index 507799328a44..507799328a44 100644 --- a/legacy-test/src/junit/framework/ComparisonFailure.java +++ b/test-base/src/junit/framework/ComparisonFailure.java diff --git a/legacy-test/src/junit/framework/Protectable.java b/test-base/src/junit/framework/Protectable.java index e1432370cfaf..e1432370cfaf 100644 --- a/legacy-test/src/junit/framework/Protectable.java +++ b/test-base/src/junit/framework/Protectable.java diff --git a/legacy-test/src/junit/framework/Test.java b/test-base/src/junit/framework/Test.java index a016ee8308f1..a016ee8308f1 100644 --- a/legacy-test/src/junit/framework/Test.java +++ b/test-base/src/junit/framework/Test.java diff --git a/legacy-test/src/junit/framework/TestCase.java b/test-base/src/junit/framework/TestCase.java index b047ec9e1afc..b047ec9e1afc 100644 --- a/legacy-test/src/junit/framework/TestCase.java +++ b/test-base/src/junit/framework/TestCase.java diff --git a/legacy-test/src/junit/framework/TestFailure.java b/test-base/src/junit/framework/TestFailure.java index 6662b1fab1b2..6662b1fab1b2 100644 --- a/legacy-test/src/junit/framework/TestFailure.java +++ b/test-base/src/junit/framework/TestFailure.java diff --git a/legacy-test/src/junit/framework/TestListener.java b/test-base/src/junit/framework/TestListener.java index 9b6944361b9d..9b6944361b9d 100644 --- a/legacy-test/src/junit/framework/TestListener.java +++ b/test-base/src/junit/framework/TestListener.java diff --git a/legacy-test/src/junit/framework/TestResult.java b/test-base/src/junit/framework/TestResult.java index 3052e94074fd..3052e94074fd 100644 --- a/legacy-test/src/junit/framework/TestResult.java +++ b/test-base/src/junit/framework/TestResult.java diff --git a/legacy-test/src/junit/framework/TestSuite.java b/test-base/src/junit/framework/TestSuite.java index 336efd1800d7..336efd1800d7 100644 --- a/legacy-test/src/junit/framework/TestSuite.java +++ b/test-base/src/junit/framework/TestSuite.java diff --git a/test-mock/Android.mk b/test-mock/Android.mk index 18da8b8bd687..e4af17cbd711 100644 --- a/test-mock/Android.mk +++ b/test-mock/Android.mk @@ -26,7 +26,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test -LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../legacy-test/jarjar-rules.txt +LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt LOCAL_MODULE:= repackaged.android.test.mock @@ -130,15 +130,4 @@ update-android-test-mock-api: $(ANDROID_TEST_MOCK_OUTPUT_API_FILE) | $(ACP) @echo Copying removed.txt $(hide) $(ACP) $(ANDROID_TEST_MOCK_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_MOCK_REMOVED_API_FILE) -# Build the android.test.mock.sdk library -# ======================================= -include $(CLEAR_VARS) - -LOCAL_MODULE := android.test.mock.sdk -LOCAL_SDK_VERSION := current - -LOCAL_STATIC_JAVA_LIBRARIES := android.test.mock.stubs - -include $(BUILD_STATIC_JAVA_LIBRARY) - endif # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true diff --git a/test-runner/Android.mk b/test-runner/Android.mk index d0f5b3248c29..c0fd7f8ec26d 100644 --- a/test-runner/Android.mk +++ b/test-runner/Android.mk @@ -46,7 +46,7 @@ LOCAL_JAVA_LIBRARIES := \ legacy-test \ android.test.mock \ -LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../legacy-test/jarjar-rules.txt +LOCAL_JARJAR_RULES := $(LOCAL_PATH)/../test-base/jarjar-rules.txt LOCAL_MODULE:= repackaged.android.test.runner diff --git a/test-runner/api/android-test-runner-current.txt b/test-runner/api/android-test-runner-current.txt index 905cfe701ab6..1170eb53ab7f 100644 --- a/test-runner/api/android-test-runner-current.txt +++ b/test-runner/api/android-test-runner-current.txt @@ -271,8 +271,6 @@ package android.test.suitebuilder { public deprecated class TestSuiteBuilder { ctor public TestSuiteBuilder(java.lang.Class); ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader); - method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>); - method public final android.test.suitebuilder.TestSuiteBuilder addRequirements(com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>...); method public final junit.framework.TestSuite build(); method public android.test.suitebuilder.TestSuiteBuilder excludePackages(java.lang.String...); method protected java.lang.String getSuiteName(); diff --git a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java index 6158e0cf14f4..2857696ef2ff 100644 --- a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java +++ b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java @@ -119,6 +119,7 @@ public class TestSuiteBuilder { * * @param predicates Predicates to add to the list of requirements. * @return The builder for method chaining. + * @hide */ public TestSuiteBuilder addRequirements(List<Predicate<TestMethod>> predicates) { this.predicates.addAll(predicates); @@ -156,7 +157,7 @@ public class TestSuiteBuilder { /** * Override the default name for the suite being built. This should generally be called if you - * call {@link #addRequirements(com.android.internal.util.Predicate[])} to make it clear which + * call {@code addRequirements(com.android.internal.util.Predicate[])} to make it clear which * tests will be included. The name you specify is automatically prefixed with the package * containing the tests to be run. If more than one package is specified, the first is used. * @@ -215,6 +216,7 @@ public class TestSuiteBuilder { * * @param predicates Predicates to add to the list of requirements. * @return The builder for method chaining. + * @hide */ public final TestSuiteBuilder addRequirements(Predicate<TestMethod>... predicates) { ArrayList<Predicate<TestMethod>> list = new ArrayList<Predicate<TestMethod>>(); diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 24187d96fec5..02ac86c94b46 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -707,7 +707,9 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config std::unique_ptr<FileReference> file_ref = util::make_unique<FileReference>(dst_pool->MakeRef( str, StringPool::Context(StringPool::Context::kHighPriority, config))); - if (util::EndsWith(*file_ref->path, ".xml")) { + if (type == ResourceType::kRaw) { + file_ref->type = ResourceFile::Type::kUnknown; + } else if (util::EndsWith(*file_ref->path, ".xml")) { file_ref->type = ResourceFile::Type::kBinaryXml; } else if (util::EndsWith(*file_ref->path, ".png")) { file_ref->type = ResourceFile::Type::kPng; diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index cca12947d26e..423d0285e96f 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -18,6 +18,10 @@ using namespace std; namespace android { namespace stats_log_api_gen { +const int PULL_ATOM_START_ID = 1000; + +int maxPushedAtomId = 2; + using android::os::statsd::Atom; // TODO: Support WorkSources @@ -195,12 +199,18 @@ write_stats_log_header(FILE* out, const Atoms& atoms) fprintf(out, " */\n"); char const* const comma = (i == atoms.decls.size() - 1) ? "" : ","; fprintf(out, " %s = %d%s\n", constant.c_str(), atom->code, comma); + if (atom->code < PULL_ATOM_START_ID && atom->code > maxPushedAtomId) { + maxPushedAtomId = atom->code; + } i++; } fprintf(out, "\n"); fprintf(out, "};\n"); fprintf(out, "\n"); + fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", + maxPushedAtomId); + // Print write methods fprintf(out, "//\n"); fprintf(out, "// Write methods\n"); diff --git a/wifi/java/android/net/wifi/BatchedScanResult.java b/wifi/java/android/net/wifi/BatchedScanResult.java index 6d9f00f3d5ca..c06543ec260a 100644 --- a/wifi/java/android/net/wifi/BatchedScanResult.java +++ b/wifi/java/android/net/wifi/BatchedScanResult.java @@ -17,6 +17,7 @@ package android.net.wifi; import android.os.Parcelable; +import android.annotation.SystemApi; import android.os.Parcel; import java.util.ArrayList; @@ -29,6 +30,7 @@ import java.util.List; * @removed */ @Deprecated +@SystemApi public class BatchedScanResult implements Parcelable { private static final String TAG = "BatchedScanResult"; |