diff options
148 files changed, 5028 insertions, 2245 deletions
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java index 47dd257b06b5..586c3852325a 100644 --- a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java @@ -46,7 +46,7 @@ public class BoringLayoutCreateDrawPerfTest { private static final float SPACING_ADD = 10f; private static final float SPACING_MULT = 1.5f; - @Parameterized.Parameters(name = "cached={3},{1} chars,{0}") + @Parameterized.Parameters(name = "cached={3},{1}chars,{0}") public static Collection cases() { final List<Object[]> params = new ArrayList<>(); for (int length : new int[]{128}) { diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java index 34de65de2627..9d11f29557d2 100644 --- a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java @@ -40,7 +40,7 @@ public class BoringLayoutIsBoringPerfTest { private static final boolean[] BOOLEANS = new boolean[]{false, true}; - @Parameterized.Parameters(name = "cached={4},{1} chars,{0}") + @Parameterized.Parameters(name = "cached={4},{1}chars,{0}") public static Collection cases() { final List<Object[]> params = new ArrayList<>(); for (int length : new int[]{128}) { diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java index 00b60add5b15..676879857491 100644 --- a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java @@ -42,7 +42,7 @@ public class PaintMeasureDrawPerfTest { private static final boolean[] BOOLEANS = new boolean[]{false, true}; - @Parameterized.Parameters(name = "cached={1},{0} chars") + @Parameterized.Parameters(name = "cached={1},{0}chars") public static Collection cases() { final List<Object[]> params = new ArrayList<>(); for (int length : new int[]{128}) { diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java index 356e2e0dab3c..bfdb7589bdff 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java @@ -50,7 +50,7 @@ public class StaticLayoutCreateDrawPerfTest { @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - @Parameterized.Parameters(name = "cached={3},{1} chars,{0}") + @Parameterized.Parameters(name = "cached={3},{1}chars,{0}") public static Collection cases() { final List<Object[]> params = new ArrayList<>(); for (int length : new int[]{128}) { diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java index a2bf33e1f607..ff2d57edb11b 100644 --- a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java +++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java @@ -40,7 +40,7 @@ import java.util.Locale; import java.util.Random; /** - * Performance test for multi line, single style {@link StaticLayout} creation/draw. + * Performance test for {@link TextView} measure/draw. */ @LargeTest @RunWith(Parameterized.class) @@ -51,7 +51,7 @@ public class TextViewSetTextMeasurePerfTest { @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - @Parameterized.Parameters(name = "cached={3},{1} chars,{0}") + @Parameterized.Parameters(name = "cached={3},{1}chars,{0}") public static Collection cases() { final List<Object[]> params = new ArrayList<>(); for (int length : new int[]{128}) { diff --git a/api/system-current.txt b/api/system-current.txt index ea077fe62aee..9e02b03c0add 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -27217,7 +27217,6 @@ package android.media.tv { } public static final class TvInputManager.Hardware { - method public boolean dispatchKeyEventToHdmi(android.view.KeyEvent); method public void overrideAudioSink(int, java.lang.String, int, int, int); method public void setStreamVolume(float); method public boolean setSurface(android.view.Surface, android.media.tv.TvStreamConfig); diff --git a/api/system-removed.txt b/api/system-removed.txt index 7ee261e88fc7..639877fae6e2 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -275,6 +275,10 @@ package android.media.tv { 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); + } + public class TvView extends android.view.ViewGroup { method public void requestUnblockContent(android.media.tv.TvContentRating); } diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index b16188e9d82e..e5d35b3b8a0e 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -34,13 +34,6 @@ LOCAL_SRC_FILES += \ iot/BootAction.cpp \ iot/BootParameters.cpp \ -LOCAL_SHARED_LIBRARIES += \ - libandroidthings \ - libbase \ - libbinder \ - -LOCAL_STATIC_LIBRARIES += cpufeatures - else LOCAL_SRC_FILES += \ diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp index 742f9c24f3a0..00cef430135e 100644 --- a/cmds/bootanimation/iot/iotbootanimation_main.cpp +++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "IotBootAnimation" -#include <android-base/file.h> +#include <base/files/file_util.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/ProcessState.h> @@ -31,13 +31,14 @@ #include "BootParameters.h" using namespace android; -using android::base::ReadFileToString; // Create a typedef for readability. typedef android::BootAnimation::Animation Animation; namespace { +constexpr const char* kDefaultLibName = "libbootaction.so"; + class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks { public: BootActionAnimationCallbacks(std::unique_ptr<BootParameters> bootParameters) @@ -49,11 +50,13 @@ public: // This value is optionally provided by the user and will be written to // /oem/oem.prop. char property[PROP_VALUE_MAX] = {0}; - if (property_get("ro.oem.bootactions.lib", property, "") < 1) { - ALOGI("No bootaction specified"); + property_get("ro.oem.bootactions.lib", property, kDefaultLibName); + library_path += property; + + if (!::base::PathExists(::base::FilePath(library_path))) { + ALOGI("Skipping boot actions: %s does not exist", library_path.c_str()); return; } - library_path += property; mBootAction = new BootAction(); if (!mBootAction->init(library_path, mBootParameters->getParameters())) { diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index ca36d27cf684..c5b3b68a44b7 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -42,24 +42,7 @@ LOCAL_MODULE := statsd LOCAL_SRC_FILES := \ ../../core/java/android/os/IStatsCompanionService.aidl \ ../../core/java/android/os/IStatsManager.aidl \ - src/StatsService.cpp \ - src/AnomalyMonitor.cpp \ - src/LogEntryPrinter.cpp \ - src/LogReader.cpp \ - src/main.cpp \ - src/DropboxWriter.cpp \ - src/parse_util.cpp \ - src/StatsLogProcessor.cpp \ - src/stats_log.proto \ - src/statsd_config.proto \ - src/StatsPullerManager.cpp \ - src/KernelWakelockPuller.cpp \ - src/DropboxReader.cpp \ - src/matchers/LogEntryMatcherManager.cpp \ - src/metrics/CountMetricProducer.cpp \ - src/metrics/ConditionTracker.cpp \ - src/metrics/MetricsManager.cpp \ - src/metrics/CountAnomalyTracker.cpp \ + $(call all-cpp-files-under,src) \ LOCAL_CFLAGS += \ -Wall \ @@ -129,13 +112,20 @@ LOCAL_SRC_FILES := \ ../../core/java/android/os/IStatsCompanionService.aidl \ ../../core/java/android/os/IStatsManager.aidl \ src/StatsService.cpp \ - tests/indexed_priority_queue_test.cpp \ - src/parse_util.cpp \ + src/stats_util.cpp \ src/LogEntryPrinter.cpp \ src/LogReader.cpp \ - src/matchers/LogEntryMatcherManager.cpp \ - tests/LogReader_test.cpp \ - tests/LogEntryMatcher_test.cpp \ + src/matchers/matcher_util.cpp \ + src/condition/SimpleConditionTracker.cpp \ + src/condition/CombinationConditionTracker.cpp \ + src/matchers/SimpleLogMatchingTracker.cpp \ + src/matchers/CombinationLogMatchingTracker.cpp \ + src/metrics/metrics_manager_util.cpp \ + src/metrics/CountMetricProducer.cpp \ + src/metrics/CountAnomalyTracker.cpp \ + src/condition/condition_util.cpp \ + src/UidMap.cpp \ + $(call all-cpp-files-under, tests) \ LOCAL_STATIC_LIBRARIES := \ libgmock \ diff --git a/core/tests/coretests/src/android/print/PrintTestActivity.java b/cmds/statsd/src/PackageInfoListener.h index e9b001f3c821..476c1d953cbc 100644 --- a/core/tests/coretests/src/android/print/PrintTestActivity.java +++ b/cmds/statsd/src/PackageInfoListener.h @@ -14,19 +14,25 @@ * limitations under the License. */ -package android.print; +#ifndef STATSD_PACKAGE_INFO_LISTENER_H +#define STATSD_PACKAGE_INFO_LISTENER_H -import android.app.Activity; -import android.os.Bundle; -import android.view.WindowManager; +#include <utils/RefBase.h> +#include <string> -public class PrintTestActivity extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); +namespace android { +namespace os { +namespace statsd { - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); - } -} +class PackageInfoListener : public virtual android::RefBase { +public: + // Uid map will notify this listener that the app with apk name and uid has been upgraded to + // the specified version. + virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int version) = 0; +}; + +} // namespace statsd +} // namespace os +} // namespace android + +#endif //STATSD_PACKAGE_INFO_LISTENER_H diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 117fb5e2cefc..f877ef30432c 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -20,7 +20,6 @@ #include <frameworks/base/cmds/statsd/src/stats_log.pb.h> #include <log/log_event_list.h> #include <metrics/CountMetricProducer.h> -#include <parse_util.h> #include <utils/Errors.h> using namespace android; @@ -32,7 +31,9 @@ namespace android { namespace os { namespace statsd { -StatsLogProcessor::StatsLogProcessor() : m_dropbox_writer("all-logs") { +StatsLogProcessor::StatsLogProcessor(const sp<UidMap> &uidMap) + : m_dropbox_writer("all-logs"), m_UidMap(uidMap) +{ // hardcoded config // this should be called from StatsService when it receives a statsd_config UpdateConfig(0, buildFakeConfig()); @@ -41,28 +42,6 @@ StatsLogProcessor::StatsLogProcessor() : m_dropbox_writer("all-logs") { StatsLogProcessor::~StatsLogProcessor() { } -StatsdConfig StatsLogProcessor::buildFakeConfig() { - // HACK: Hard code a test metric for counting screen on events... - StatsdConfig config; - config.set_config_id(12345L); - - CountMetric* metric = config.add_count_metric(); - metric->set_metric_id(20150717L); - metric->set_what("SCREEN_IS_ON"); - metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - - LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); - eventMatcher->set_name("SCREEN_IS_ON"); - - SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); - simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher() - ->set_key(1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleLogEntryMatcher->mutable_key_value_matcher(0) - ->set_eq_int(2/*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); - return config; -} - // TODO: what if statsd service restarts? How do we know what logs are already processed before? void StatsLogProcessor::OnLogEvent(const log_msg& msg) { // TODO: Use EventMetric to filter the events we want to log. @@ -83,7 +62,14 @@ void StatsLogProcessor::UpdateConfig(const int config_source, const StatsdConfig ALOGD("Updated configuration for source %i", config_source); - mMetricsManagers.insert({config_source, std::make_unique<MetricsManager>(config)}); + unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config); + if (newMetricsManager->isConfigValid()) { + mMetricsManagers.insert({config_source, std::move(newMetricsManager)}); + ALOGD("StatsdConfig valid"); + } else { + // If there is any error in the config, don't use it. + ALOGD("StatsdConfig NOT valid"); + } } } // namespace statsd diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 88c63fa5a149..05e441caa496 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -16,11 +16,12 @@ #ifndef STATS_LOG_PROCESSOR_H #define STATS_LOG_PROCESSOR_H -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "DropboxWriter.h" #include "LogReader.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "metrics/MetricsManager.h" -#include "parse_util.h" +#include "stats_util.h" +#include "UidMap.h" #include <log/logprint.h> #include <stdio.h> @@ -32,7 +33,7 @@ namespace statsd { class StatsLogProcessor : public LogListener { public: - StatsLogProcessor(); + StatsLogProcessor(const sp<UidMap> &uidMap); virtual ~StatsLogProcessor(); virtual void OnLogEvent(const log_msg& msg); @@ -45,7 +46,7 @@ private: std::unordered_map<int, std::unique_ptr<MetricsManager>> mMetricsManagers; - static StatsdConfig buildFakeConfig(); + sp<UidMap> m_UidMap; // Reference to the UidMap to lookup app name and version for each uid. }; } // namespace statsd diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 9baeebbd554f..b496404962d5 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -40,7 +40,8 @@ namespace os { namespace statsd { StatsService::StatsService(const sp<Looper>& handlerLooper) - : mAnomalyMonitor(new AnomalyMonitor(2)), mStatsPullerManager() // TODO: Change this based on the config + : mAnomalyMonitor(new AnomalyMonitor(2)),m_UidMap(new UidMap()), mStatsPullerManager() + // TODO: Change AnomalyMonitor initialization based on the config { ALOGD("stats service constructed"); } @@ -131,6 +132,9 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& if (!args[0].compare(String8("config"))) { return doLoadConfig(in); } + if (!args[0].compare(String8("print-uid-map"))) { + return doPrintUidMap(out); + } } printCmdHelp(out); @@ -153,6 +157,43 @@ status_t StatsService::doLoadConfig(FILE* in) { } } +Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version, + const vector<String16>& app) { + if (DEBUG) ALOGD("StatsService::informAllUidData was called"); + + if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { + return Status::fromExceptionCode(Status::EX_SECURITY, + "Only system uid can call informAllUidData"); + } + + m_UidMap->updateMap(uid, version, app); + if (DEBUG) ALOGD("StatsService::informAllUidData succeeded"); + + return Status::ok(); +} + +Status StatsService::informOnePackage(const String16& app, int32_t uid, int32_t version) { + if (DEBUG) ALOGD("StatsService::informOnePackage was called"); + + if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { + return Status::fromExceptionCode(Status::EX_SECURITY, + "Only system uid can call informOnePackage"); + } + m_UidMap->updateApp(app, uid, version); + return Status::ok(); +} + +Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) { + if (DEBUG) ALOGD("StatsService::informOnePackageRemoved was called"); + + if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) { + return Status::fromExceptionCode(Status::EX_SECURITY, + "Only system uid can call informOnePackageRemoved"); + } + m_UidMap->removeApp(app, uid); + return Status::ok(); +} + Status StatsService::informAnomalyAlarmFired() { if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired was called"); @@ -261,9 +302,15 @@ status_t StatsService::doPrintStatsLog(FILE* out, const Vector<String8>& args) { return DropboxReader::readStatsLogs(out, args[1].string(), msec); } +status_t StatsService::doPrintUidMap(FILE* out) { + m_UidMap->printUidMap(out); + return NO_ERROR; +} + void StatsService::printCmdHelp(FILE* out) { fprintf(out, "Usage:\n"); fprintf(out, "\t print-stats-log [tag_required] [timestamp_nsec_optional]\n"); + fprintf(out, "\t print-uid-map Prints the UID, app name, version mapping.\n"); fprintf(out, "\t config\t Loads a new config from command-line (must be proto in wire-encoded " "format).\n"); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 964227975fe8..541f7e8be7fa 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -20,6 +20,8 @@ #include "AnomalyMonitor.h" #include "StatsLogProcessor.h" #include "StatsPullerManager.h" +#include "StatsPuller.h" +#include "UidMap.h" #include <android/os/BnStatsManager.h> #include <android/os/IStatsCompanionService.h> @@ -60,6 +62,11 @@ public: virtual Status informPollAlarmFired(); + virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version, + const vector<String16>& app); + virtual Status informOnePackage(const String16& app, int32_t uid, int32_t version); + virtual Status informOnePackageRemoved(const String16& app, int32_t uid); + virtual status_t setProcessor(const sp<StatsLogProcessor>& main_processor); // TODO: public for testing since statsd doesn't run when system starts. Change to private @@ -71,10 +78,16 @@ public: // TODO: Should be private. Temporarily public for testing purposes only. const sp<AnomalyMonitor> mAnomalyMonitor; + sp<UidMap> getUidMap() { + return m_UidMap; + } + /** Fetches and returns the StatsCompanionService. */ static sp<IStatsCompanionService> getStatsCompanionService(); private: + sp<UidMap> m_UidMap; // Reference to the UID map needed for translating UID to app name/version. + sp<StatsLogProcessor> m_processor; // Reference to the processor for updating configs. status_t doPrintStatsLog(FILE* out, const Vector<String8>& args); @@ -84,6 +97,8 @@ private: status_t doLoadConfig(FILE* in); StatsPullerManager mStatsPullerManager; + + status_t doPrintUidMap(FILE* out); }; // --- StatsdDeathRecipient --- diff --git a/cmds/statsd/src/UidMap.cpp b/cmds/statsd/src/UidMap.cpp new file mode 100644 index 000000000000..76a7f3f28dee --- /dev/null +++ b/cmds/statsd/src/UidMap.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, versionCode 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 "UidMap.h" +#include <cutils/log.h> +#include <utils/Errors.h> + +using namespace android; + +namespace android { +namespace os { +namespace statsd { + +bool UidMap::hasApp(int uid, const string& packageName) const { + lock_guard<mutex> lock(mMutex); + + auto range = mMap.equal_range(uid); + for (auto it = range.first; it != range.second; ++it) { + if (it->second.packageName == packageName) { + return true; + } + } + return false; +} + +int UidMap::getAppVersion(int uid, const string& packageName) const { + lock_guard<mutex> lock(mMutex); + + auto range = mMap.equal_range(uid); + for (auto it = range.first; it != range.second; ++it) { + if (it->second.packageName == packageName) { + return it->second.versionCode; + } + } + return 0; +} + +void UidMap::updateMap(const vector <int32_t> &uid, const vector <int32_t> &versionCode, + const vector <String16> &packageName) { + lock_guard<mutex> lock(mMutex); // Exclusively lock for updates. + + mMap.clear(); + for (unsigned long j=0; j<uid.size(); j++) { + mMap.insert(make_pair(uid[j], AppData(string(String8(packageName[j]).string()), + versionCode[j]))); + } + + if (mOutput.initial_size() == 0) { // Provide the initial states in the mOutput proto + for (unsigned long j=0; j<uid.size(); j++) { + auto t = mOutput.add_initial(); + t->set_app(string(String8(packageName[j]).string())); + t->set_version(int(versionCode[j])); + t->set_uid(uid[j]); + } + } +} + +void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode){ + lock_guard<mutex> lock(mMutex); + + string app = string(String8(app_16).string()); + + // Notify any interested producers that this app has updated + for (auto it : mSubscribers) { + it->notifyAppUpgrade(app, uid, versionCode); + } + + auto log = mOutput.add_changes(); + log->set_deletion(false); + //log.timestamp = TODO: choose how timestamps are computed + log->set_app(app); + log->set_uid(uid); + log->set_version(versionCode); + + auto range = mMap.equal_range(int(uid)); + for (auto it = range.first; it != range.second; ++it) { + if (it->second.packageName == app) { + it->second.versionCode = int(versionCode); + return; + } + ALOGD("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid); + return; + } + + // Otherwise, we need to add an app at this uid. + mMap.insert(make_pair(uid, AppData(app, int(versionCode)))); +} + + +void UidMap::removeApp(const String16& app_16, const int32_t& uid){ + lock_guard<mutex> lock(mMutex); + + string app = string(String8(app_16).string()); + + auto log = mOutput.add_changes(); + log->set_deletion(true); + //log.timestamp = TODO: choose how timestamps are computed + log->set_app(app); + log->set_uid(uid); + + auto range = mMap.equal_range(int(uid)); + for (auto it = range.first; it != range.second; ++it) { + if (it->second.packageName == app) { + mMap.erase(it); + return; + } + } + ALOGD("removeApp failed to find the app %s with uid %i to remove", app.c_str(), uid); + return; +} + +void UidMap::addListener(sp<PackageInfoListener> producer) { + lock_guard<mutex> lock(mMutex); // Lock for updates + mSubscribers.insert(producer); +} + +void UidMap::removeListener(sp<PackageInfoListener> producer) { + lock_guard<mutex> lock(mMutex); // Lock for updates + mSubscribers.erase(producer); +} + +UidMapping UidMap::getAndClearOutput() { + lock_guard<mutex> lock(mMutex); // Lock for updates + + auto ret = UidMapping(mOutput); // Copy that will be returned. + mOutput.Clear(); + + // Re-initialize the initial state for the outputs. This results in extra data being uploaded + // but helps ensure we can't re-construct the UID->app name, versionCode mapping in server. + for (auto it : mMap) { + auto t = mOutput.add_initial(); + t->set_app(it.second.packageName); + t->set_version(it.second.versionCode); + t->set_uid(it.first); + } + + return ret; +} + +void UidMap::printUidMap(FILE* out) { + lock_guard<mutex> lock(mMutex); + + for (auto it : mMap) { + fprintf(out, "%s, v%d (%i)\n", it.second.packageName.c_str(), it.second.versionCode, it.first); + } +} + + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/UidMap.h b/cmds/statsd/src/UidMap.h new file mode 100644 index 000000000000..1481010a60b8 --- /dev/null +++ b/cmds/statsd/src/UidMap.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef STATSD_UIDMAP_H +#define STATSD_UIDMAP_H + +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "PackageInfoListener.h" + +#include <binder/IResultReceiver.h> +#include <binder/IShellCallback.h> +#include <log/logprint.h> +#include <mutex> +#include <string> +#include <stdio.h> +#include <set> +#include <unordered_map> +#include <utils/RefBase.h> + +using namespace std; + +namespace android { +namespace os { +namespace statsd { + +struct AppData { + const string packageName; + int versionCode; + + AppData(const string& a, const int v) : packageName(a), versionCode(v) {}; +}; + +// UidMap keeps track of what the corresponding app name (APK name) and version code for every uid +// at any given moment. This map must be updated by StatsCompanionService. +class UidMap : public virtual android::RefBase { +public: + /* + * All three inputs must be the same size, and the jth element in each array refers to the same + * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j]. + */ + void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode, + const vector<String16>& packageName); + + // Returns true if the given uid contains the specified app (eg. com.google.android.gms). + bool hasApp(int uid, const string& packageName) const; + + int getAppVersion(int uid, const string& packageName) const; + + void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode); + void removeApp(const String16& packageName, const int32_t& uid); + + // Helper for debugging contents of this uid map. Can be triggered with: + // adb shell cmd stats print-uid-map + void printUidMap(FILE* out); + + // Commands for indicating to the map that a producer should be notified if an app is updated. + // This allows the metric producer to distinguish when the same uid or app represents a + // different version of an app. + void addListener(sp<PackageInfoListener> producer); + // Remove the listener from the set of metric producers that subscribe to updates. + void removeListener(sp<PackageInfoListener> producer); + + // Grabs the current output contents and then clears it. + UidMapping getAndClearOutput(); + +private: + // TODO: Use shared_mutex for improved read-locking if a library can be found in Android. + mutable mutex mMutex; + + std::unordered_multimap<int, AppData> mMap; + + // We prepare the output proto as apps are updated, so that we can grab the current output. + UidMapping mOutput; + + // Metric producers that should be notified if there's an upgrade in any app. + set<sp<PackageInfoListener>> mSubscribers; +}; + +} // namespace statsd +} // namespace os +} // namespace android + +#endif //STATSD_UIDMAP_H + diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp new file mode 100644 index 000000000000..6188383d4e8d --- /dev/null +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -0,0 +1,136 @@ +/* + * 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. + */ +#define LOG_TAG "CombinationConditionTracker" +#define DEBUG true // STOPSHIP if true +#define VLOG(...) \ + if (DEBUG) ALOGD(__VA_ARGS__); + +#include "CombinationConditionTracker.h" +#include <cutils/log.h> +#include <log/logprint.h> +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::vector; + +namespace android { +namespace os { +namespace statsd { + +CombinationConditionTracker::CombinationConditionTracker(const string& name, const int index) + : ConditionTracker(name, index) { + VLOG("creating CombinationConditionTracker %s", mName.c_str()); +} + +CombinationConditionTracker::~CombinationConditionTracker() { + VLOG("~CombinationConditionTracker() %s", mName.c_str()); +} + +bool CombinationConditionTracker::init(const vector<Condition>& allConditionConfig, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<string, int>& conditionNameIndexMap, + vector<bool>& stack) { + VLOG("Combiniation condition init() %s", mName.c_str()); + if (mInitialized) { + return true; + } + + // mark this node as visited in the recursion stack. + stack[mIndex] = true; + + Condition_Combination combinationCondition = allConditionConfig[mIndex].combination(); + + if (!combinationCondition.has_operation()) { + return false; + } + mLogicalOperation = combinationCondition.operation(); + + if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.condition_size() != 1) { + return false; + } + + for (string child : combinationCondition.condition()) { + auto it = conditionNameIndexMap.find(child); + + if (it == conditionNameIndexMap.end()) { + ALOGW("Condition %s not found in the config", child.c_str()); + return false; + } + + int childIndex = it->second; + const auto& childTracker = allConditionTrackers[childIndex]; + // if the child is a visited node in the recursion -> circle detected. + if (stack[childIndex]) { + ALOGW("Circle detected!!!"); + return false; + } + + bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers, + conditionNameIndexMap, stack); + + if (!initChildSucceeded) { + ALOGW("Child initialization failed %s ", child.c_str()); + return false; + } else { + ALOGW("Child initialization success %s ", child.c_str()); + } + + mChildren.push_back(childIndex); + + mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(), + childTracker->getLogTrackerIndex().end()); + } + + // unmark this node in the recursion stack. + stack[mIndex] = false; + + mInitialized = true; + + return true; +} + +bool CombinationConditionTracker::evaluateCondition( + const LogEventWrapper& event, const std::vector<MatchingState>& eventMatcherValues, + const std::vector<sp<ConditionTracker>>& mAllConditions, + std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache) { + // value is up to date. + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { + return false; + } + + for (const int childIndex : mChildren) { + if (conditionCache[childIndex] == ConditionState::kNotEvaluated) { + const sp<ConditionTracker>& child = mAllConditions[childIndex]; + child->evaluateCondition(event, eventMatcherValues, mAllConditions, conditionCache, + changedCache); + } + } + + ConditionState newCondition = + evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache); + + bool changed = (mConditionState != newCondition); + mConditionState = newCondition; + + conditionCache[mIndex] = mConditionState; + + changedCache[mIndex] = changed; + return changed; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h new file mode 100644 index 000000000000..38780e7062ed --- /dev/null +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef COMBINATION_CONDITION_TRACKER_H +#define COMBINATION_CONDITION_TRACKER_H + +#include "ConditionTracker.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +namespace android { +namespace os { +namespace statsd { + +class CombinationConditionTracker : public virtual ConditionTracker { +public: + CombinationConditionTracker(const std::string& name, const int index); + + ~CombinationConditionTracker(); + + bool init(const std::vector<Condition>& allConditionConfig, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<std::string, int>& conditionNameIndexMap, + std::vector<bool>& stack) override; + + bool evaluateCondition(const LogEventWrapper& event, + const std::vector<MatchingState>& eventMatcherValues, + const std::vector<sp<ConditionTracker>>& mAllConditions, + std::vector<ConditionState>& conditionCache, + std::vector<bool>& changedCache) override; + +private: + LogicalOperation mLogicalOperation; + // Store index of the children Conditions. + // We don't store string name of the Children, because we want to get rid of the hash map to + // map the name to object. We don't want to store smart pointers to children, because it + // increases the risk of circular dependency and memory leak. + std::vector<int> mChildren; +}; + +} // namespace statsd +} // namespace os +} // namespace android + +#endif // COMBINATION_CONDITION_TRACKER_H diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h new file mode 100644 index 000000000000..2da8fa0e8655 --- /dev/null +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#ifndef CONDITION_TRACKER_H +#define CONDITION_TRACKER_H + +#include <cutils/log.h> +#include <log/logprint.h> +#include <utils/RefBase.h> +#include <unordered_map> +#include "../matchers/LogMatchingTracker.h" +#include "../matchers/matcher_util.h" +#include "condition_util.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +namespace android { +namespace os { +namespace statsd { + +class ConditionTracker : public virtual RefBase { +public: + ConditionTracker(const std::string& name, const int index) + : mName(name), + mIndex(index), + mInitialized(false), + mConditionState(ConditionState::kUnknown), + mTrackerIndex(){}; + + virtual ~ConditionTracker(){}; + + // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also + // be done in the constructor, but we do it separately because (1) easy to return a bool to + // indicate whether the initialization is successful. (2) makes unit test easier. + // allConditionConfig: the list of all Condition config from statsd_config. + // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also + // need to call init() on children conditions) + // conditionNameIndexMap: the mapping from condition name to its index. + // stack: a bit map to keep track which nodes have been visited on the stack in the recursion. + virtual bool init(const std::vector<Condition>& allConditionConfig, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<std::string, int>& conditionNameIndexMap, + std::vector<bool>& stack) = 0; + + // evaluate current condition given the new event. + // return true if the condition state changed, false if the condition state is not changed. + // event: the new log event + // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process + // event before ConditionTrackers, because ConditionTracker depends on + // LogMatchingTrackers. + // mAllConditions: the list of all ConditionTracker + // conditionCache: the cached results of the ConditionTrackers for this new event. + // changedCache: the bit map to record whether the condition has changed. + virtual bool evaluateCondition(const LogEventWrapper& event, + const std::vector<MatchingState>& eventMatcherValues, + const std::vector<sp<ConditionTracker>>& mAllConditions, + std::vector<ConditionState>& conditionCache, + std::vector<bool>& changedCache) = 0; + + // Return the current condition state. + virtual ConditionState isConditionMet() { + ALOGW("Condition %s value %d", mName.c_str(), mConditionState); + return mConditionState; + }; + + // return the list of LogMatchingTracker index that this ConditionTracker uses. + virtual const std::set<int>& getLogTrackerIndex() const { + return mTrackerIndex; + } + +protected: + // We don't really need the string name, but having a name here makes log messages + // easy to debug. + const std::string mName; + + // the index of this condition in the manager's condition list. + const int mIndex; + + // if it's properly initialized. + bool mInitialized; + + // current condition state. + ConditionState mConditionState; + + // the list of LogMatchingTracker index that this ConditionTracker uses. + std::set<int> mTrackerIndex; +}; + +} // namespace statsd +} // namespace os +} // namespace android + +#endif // CONDITION_TRACKER_H diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp new file mode 100644 index 000000000000..e78c0de7bdf5 --- /dev/null +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -0,0 +1,133 @@ +/* + * 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. + */ + +#define LOG_TAG "Stats_SimpleConditionTracker" +#define DEBUG true // STOPSHIP if true +#define VLOG(...) \ + if (DEBUG) ALOGD(__VA_ARGS__); + +#include "SimpleConditionTracker.h" +#include <cutils/log.h> +#include <log/logprint.h> + +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::vector; + +namespace android { +namespace os { +namespace statsd { + +SimpleConditionTracker::SimpleConditionTracker( + const string& name, const int index, const SimpleCondition& simpleCondition, + const unordered_map<string, int>& trackerNameIndexMap) + : ConditionTracker(name, index) { + VLOG("creating SimpleConditionTracker %s", mName.c_str()); + mCountNesting = simpleCondition.count_nesting(); + + if (simpleCondition.has_start()) { + auto pair = trackerNameIndexMap.find(simpleCondition.start()); + if (pair == trackerNameIndexMap.end()) { + ALOGW("Start matcher %s not found in the config", simpleCondition.start().c_str()); + return; + } + mStartLogMatcherIndex = pair->second; + mTrackerIndex.insert(mStartLogMatcherIndex); + } else { + mStartLogMatcherIndex = -1; + } + + if (simpleCondition.has_stop()) { + auto pair = trackerNameIndexMap.find(simpleCondition.stop()); + if (pair == trackerNameIndexMap.end()) { + ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str()); + return; + } + mStopLogMatcherIndex = pair->second; + mTrackerIndex.insert(mStopLogMatcherIndex); + } else { + mStopLogMatcherIndex = -1; + } + + if (simpleCondition.has_stop_all()) { + auto pair = trackerNameIndexMap.find(simpleCondition.stop_all()); + if (pair == trackerNameIndexMap.end()) { + ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str()); + return; + } + mStopAllLogMatcherIndex = pair->second; + mTrackerIndex.insert(mStopAllLogMatcherIndex); + } else { + mStopAllLogMatcherIndex = -1; + } + + mInitialized = true; +} + +SimpleConditionTracker::~SimpleConditionTracker() { + VLOG("~SimpleConditionTracker()"); +} + +bool SimpleConditionTracker::init(const vector<Condition>& allConditionConfig, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<string, int>& conditionNameIndexMap, + vector<bool>& stack) { + // SimpleConditionTracker does not have dependency on other conditions, thus we just return + // if the initialization was successful. + return mInitialized; +} + +bool SimpleConditionTracker::evaluateCondition(const LogEventWrapper& event, + const vector<MatchingState>& eventMatcherValues, + const vector<sp<ConditionTracker>>& mAllConditions, + vector<ConditionState>& conditionCache, + vector<bool>& changedCache) { + if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { + // it has been evaluated. + VLOG("Yes, already evaluated, %s %d", mName.c_str(), mConditionState); + return false; + } + + // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions. + ConditionState newCondition = mConditionState; + // Note: The order to evaluate the following start, stop, stop_all matters. + // The priority of overwrite is stop_all > stop > start. + if (mStartLogMatcherIndex >= 0 && + eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) { + newCondition = ConditionState::kTrue; + } + + if (mStopLogMatcherIndex >= 0 && + eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) { + newCondition = ConditionState::kFalse; + } + + if (mStopAllLogMatcherIndex >= 0 && + eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) { + newCondition = ConditionState::kFalse; + } + + bool changed = (mConditionState != newCondition); + mConditionState = newCondition; + conditionCache[mIndex] = mConditionState; + changedCache[mIndex] = changed; + return changed; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h new file mode 100644 index 000000000000..41e17076cbdc --- /dev/null +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef SIMPLE_CONDITION_TRACKER_H +#define SIMPLE_CONDITION_TRACKER_H + +#include "ConditionTracker.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +namespace android { +namespace os { +namespace statsd { + +class SimpleConditionTracker : public virtual ConditionTracker { +public: + SimpleConditionTracker(const std::string& name, const int index, + const SimpleCondition& simpleCondition, + const std::unordered_map<std::string, int>& trackerNameIndexMap); + + ~SimpleConditionTracker(); + + bool init(const std::vector<Condition>& allConditionConfig, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<std::string, int>& conditionNameIndexMap, + std::vector<bool>& stack) override; + + bool evaluateCondition(const LogEventWrapper& event, + const std::vector<MatchingState>& eventMatcherValues, + const std::vector<sp<ConditionTracker>>& mAllConditions, + std::vector<ConditionState>& conditionCache, + std::vector<bool>& changedCache) override; + +private: + // The index of the LogEventMatcher which defines the start. + int mStartLogMatcherIndex; + + // The index of the LogEventMatcher which defines the end. + int mStopLogMatcherIndex; + + // if the start end needs to be nested. + bool mCountNesting; + + // The index of the LogEventMatcher which defines the stop all. + int mStopAllLogMatcherIndex; +}; + +} // namespace statsd +} // namespace os +} // namespace android + +#endif // SIMPLE_CONDITION_TRACKER_H diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp new file mode 100644 index 000000000000..cb07d1530dab --- /dev/null +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -0,0 +1,93 @@ +/* + * 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 "condition_util.h" + +#include <cutils/log.h> +#include <log/event_tag_map.h> +#include <log/log_event_list.h> +#include <log/logprint.h> +#include <utils/Errors.h> +#include <unordered_map> +#include "ConditionTracker.h" +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "stats_util.h" + +using std::set; +using std::string; +using std::unordered_map; +using std::vector; + +namespace android { +namespace os { +namespace statsd { + +ConditionState evaluateCombinationCondition(const std::vector<int>& children, + const LogicalOperation& operation, + const std::vector<ConditionState>& conditionCache) { + ConditionState newCondition; + + bool hasUnknown = false; + bool hasFalse = false; + bool hasTrue = false; + + for (auto childIndex : children) { + ConditionState childState = conditionCache[childIndex]; + if (childState == ConditionState::kUnknown) { + hasUnknown = true; + break; + } + if (childState == ConditionState::kFalse) { + hasFalse = true; + } + if (childState == ConditionState::kTrue) { + hasTrue = true; + } + } + + // If any child condition is in unknown state, the condition is unknown too. + if (hasUnknown) { + return ConditionState::kUnknown; + } + + switch (operation) { + case LogicalOperation::AND: { + newCondition = hasFalse ? ConditionState::kFalse : ConditionState::kTrue; + break; + } + case LogicalOperation::OR: { + newCondition = hasTrue ? ConditionState::kTrue : ConditionState::kFalse; + break; + } + case LogicalOperation::NOT: + newCondition = (conditionCache[children[0]] == ConditionState::kFalse) + ? ConditionState::kTrue + : ConditionState::kFalse; + break; + case LogicalOperation::NAND: + newCondition = hasFalse ? ConditionState::kTrue : ConditionState::kFalse; + break; + case LogicalOperation::NOR: + newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue; + break; + } + return newCondition; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/metrics/ConditionTracker.h b/cmds/statsd/src/condition/condition_util.h index b94d5abc2cf5..a4fcea38d6b5 100644 --- a/cmds/statsd/src/metrics/ConditionTracker.h +++ b/cmds/statsd/src/condition/condition_util.h @@ -14,38 +14,28 @@ * limitations under the License. */ -#ifndef CONDITION_TRACKER_H -#define CONDITION_TRACKER_H +#ifndef CONDITION_UTIL_H +#define CONDITION_UTIL_H -#include <utils/RefBase.h> -#include "../matchers/LogEntryMatcherManager.h" +#include <vector> +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { namespace os { namespace statsd { -class ConditionTracker : public RefBase { -public: - ConditionTracker(); - - ConditionTracker(const Condition& condition); - - ~ConditionTracker(); - - void evaluateCondition(const LogEventWrapper& event); - - bool isConditionMet() const; - -private: - // this is the definition of the Condition. - Condition mCondition; - - bool mIsConditionMet; +enum ConditionState { + kNotEvaluated = -2, + kUnknown = -1, + kFalse = 0, + kTrue = 1, }; +ConditionState evaluateCombinationCondition(const std::vector<int>& children, + const LogicalOperation& operation, + const std::vector<ConditionState>& conditionCache); } // namespace statsd } // namespace os } // namespace android - -#endif // CONDITION_TRACKER_H +#endif // CONDITION_UTIL_H diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index b303321d8ef2..37477dc50e4e 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -20,6 +20,7 @@ #include "LogReader.h" #include "StatsLogProcessor.h" #include "StatsService.h" +#include "UidMap.h" #include <binder/IInterface.h> #include <binder/IPCThreadState.h> @@ -56,7 +57,7 @@ static void* log_reader_thread_func(void* cookie) { // Put the printer one first, so it will print before the real ones. reader->AddListener(new LogEntryPrinter(STDOUT_FILENO)); - sp<StatsLogProcessor> main_processor = new StatsLogProcessor(); + sp<StatsLogProcessor> main_processor = new StatsLogProcessor(data->service->getUidMap()); data->service->setProcessor(main_processor); reader->AddListener(main_processor); diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp new file mode 100644 index 000000000000..9f9b648ae1a5 --- /dev/null +++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp @@ -0,0 +1,121 @@ +/* + * 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 "CombinationLogMatchingTracker.h" + +#include <cutils/log.h> +#include "matcher_util.h" +using std::set; +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::vector; + +namespace android { +namespace os { +namespace statsd { + +CombinationLogMatchingTracker::CombinationLogMatchingTracker(const string& name, const int index) + : LogMatchingTracker(name, index) { +} + +CombinationLogMatchingTracker::~CombinationLogMatchingTracker() { +} + +bool CombinationLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers, + const vector<sp<LogMatchingTracker>>& allTrackers, + const unordered_map<string, int>& matcherMap, + vector<bool>& stack) { + if (mInitialized) { + return true; + } + + // mark this node as visited in the recursion stack. + stack[mIndex] = true; + + LogEntryMatcher_Combination matcher = allLogMatchers[mIndex].combination(); + + // LogicalOperation is missing in the config + if (!matcher.has_operation()) { + return false; + } + + mLogicalOperation = matcher.operation(); + + if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) { + return false; + } + + for (const string& child : matcher.matcher()) { + auto pair = matcherMap.find(child); + if (pair == matcherMap.end()) { + ALOGW("Matcher %s not found in the config", child.c_str()); + return false; + } + + int childIndex = pair->second; + + // if the child is a visited node in the recursion -> circle detected. + if (stack[childIndex]) { + ALOGE("Circle detected in matcher config"); + return false; + } + + if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) { + ALOGW("child matcher init failed %s", child.c_str()); + return false; + } + + mChildren.push_back(childIndex); + + const set<int>& childTagIds = allTrackers[childIndex]->getTagIds(); + mTagIds.insert(childTagIds.begin(), childTagIds.end()); + } + + mInitialized = true; + // unmark this node in the recursion stack. + stack[mIndex] = false; + return true; +} + +void CombinationLogMatchingTracker::onLogEvent(const LogEventWrapper& event, + const vector<sp<LogMatchingTracker>>& allTrackers, + vector<MatchingState>& matcherResults) { + // this event has been processed. + if (matcherResults[mIndex] != MatchingState::kNotComputed) { + return; + } + + if (mTagIds.find(event.tagId) == mTagIds.end()) { + matcherResults[mIndex] = MatchingState::kNotMatched; + return; + } + + // evaluate children matchers if they haven't been evaluated. + for (const int childIndex : mChildren) { + if (matcherResults[childIndex] == MatchingState::kNotComputed) { + const sp<LogMatchingTracker>& child = allTrackers[childIndex]; + child->onLogEvent(event, allTrackers, matcherResults); + } + } + + bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults); + matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h new file mode 100644 index 000000000000..51ee2328ee19 --- /dev/null +++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h @@ -0,0 +1,57 @@ +/* + * 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. + */ +#ifndef COMBINATION_LOG_MATCHING_TRACKER_H +#define COMBINATION_LOG_MATCHING_TRACKER_H + +#include <log/log_read.h> +#include <log/logprint.h> +#include <set> +#include <unordered_map> +#include <vector> +#include "LogMatchingTracker.h" +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +namespace android { +namespace os { +namespace statsd { + +// Represents a LogEntryMatcher_Combination in the StatsdConfig. +class CombinationLogMatchingTracker : public virtual LogMatchingTracker { +public: + CombinationLogMatchingTracker(const std::string& name, const int index); + + bool init(const std::vector<LogEntryMatcher>& allLogMatchers, + const std::vector<sp<LogMatchingTracker>>& allTrackers, + const std::unordered_map<std::string, int>& matcherMap, + std::vector<bool>& stack); + + ~CombinationLogMatchingTracker(); + + void onLogEvent(const LogEventWrapper& event, + const std::vector<sp<LogMatchingTracker>>& allTrackers, + std::vector<MatchingState>& matcherResults) override; + +private: + LogicalOperation mLogicalOperation; + + std::vector<int> mChildren; +}; + +} // namespace statsd +} // namespace os +} // namespace android +#endif // COMBINATION_LOG_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h new file mode 100644 index 000000000000..4244bd597b46 --- /dev/null +++ b/cmds/statsd/src/matchers/LogMatchingTracker.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#ifndef LOG_MATCHING_TRACKER_H +#define LOG_MATCHING_TRACKER_H + +#include <log/log_read.h> +#include <log/logprint.h> +#include <utils/RefBase.h> +#include <set> +#include <unordered_map> + +#include <vector> +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matcher_util.h" + +namespace android { +namespace os { +namespace statsd { + +class LogMatchingTracker : public virtual RefBase { +public: + LogMatchingTracker(const std::string& name, const int index) + : mName(name), mIndex(index), mInitialized(false){}; + + virtual ~LogMatchingTracker(){}; + + // Initialize this LogMatchingTracker. + // allLogMatchers: the list of the LogEntryMatcher proto config. This is needed because we don't + // store the proto object in memory. We only need it during initilization. + // allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with + // allLogMatchers. This is needed because the initialization is done recursively + // for CombinationLogMatchingTrackers using DFS. + // stack: a bit map to record which matcher has been visited on the stack. This is for detecting + // circle dependency. + virtual bool init(const std::vector<LogEntryMatcher>& allLogMatchers, + const std::vector<sp<LogMatchingTracker>>& allTrackers, + const std::unordered_map<std::string, int>& matcherMap, + std::vector<bool>& stack) = 0; + + // Called when a log event comes. + // event: the log event. + // allTrackers: the list of all LogMatchingTrackers. This is needed because the log processing + // is done recursively. + // matcherResults: The cached results for all matchers for this event. Parent matchers can + // directly access the children's matching results if they have been evaluated. + // Otherwise, call children matchers' onLogEvent. + virtual void onLogEvent(const LogEventWrapper& event, + const std::vector<sp<LogMatchingTracker>>& allTrackers, + std::vector<MatchingState>& matcherResults) = 0; + + // Get the tagIds that this matcher cares about. The combined collection is stored + // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses + // some memory but hopefully it can save us much CPU time when there is flood of events. + virtual const std::set<int>& getTagIds() const { + return mTagIds; + } + +protected: + // Name of this matching. We don't really need the name, but it makes log message easy to debug. + const std::string mName; + + // Index of this LogMatchingTracker in MetricsManager's container. + const int mIndex; + + // Whether this LogMatchingTracker has been properly initialized. + bool mInitialized; + + // The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly + // return kNotMatched when we receive an event with an id not in the list. This is especially + // useful when we have a complex CombinationLogMatcherTracker. + // TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a + // LogMatchingTracker cares is only a few. + std::set<int> mTagIds; +}; + +} // namespace statsd +} // namespace os +} // namespace android + +#endif // LOG_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp new file mode 100644 index 000000000000..1c83039072da --- /dev/null +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#define LOG_TAG "SimpleLogMatchingTracker" +#define DEBUG true // STOPSHIP if true +#define VLOG(...) \ + if (DEBUG) ALOGD(__VA_ARGS__); + +#include "SimpleLogMatchingTracker.h" +#include <cutils/log.h> +#include <log/logprint.h> + +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::vector; + +namespace android { +namespace os { +namespace statsd { + +SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index, + const SimpleLogEntryMatcher& matcher) + : LogMatchingTracker(name, index), mMatcher(matcher) { + for (int i = 0; i < matcher.tag_size(); i++) { + mTagIds.insert(matcher.tag(i)); + } + mInitialized = true; +} + +SimpleLogMatchingTracker::~SimpleLogMatchingTracker() { +} + +bool SimpleLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers, + const vector<sp<LogMatchingTracker>>& allTrackers, + const unordered_map<string, int>& matcherMap, + vector<bool>& stack) { + // no need to do anything. + return true; +} + +void SimpleLogMatchingTracker::onLogEvent(const LogEventWrapper& event, + const vector<sp<LogMatchingTracker>>& allTrackers, + vector<MatchingState>& matcherResults) { + if (matcherResults[mIndex] != MatchingState::kNotComputed) { + VLOG("Matcher %s already evaluated ", mName.c_str()); + return; + } + + if (mTagIds.find(event.tagId) == mTagIds.end()) { + matcherResults[mIndex] = MatchingState::kNotMatched; + return; + } + + bool matched = matchesSimple(mMatcher, event); + matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; + VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h new file mode 100644 index 000000000000..65dbe6476565 --- /dev/null +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef SIMPLE_LOG_MATCHING_TRACKER_H +#define SIMPLE_LOG_MATCHING_TRACKER_H + +#include <log/log_read.h> +#include <log/logprint.h> +#include <set> +#include <unordered_map> +#include <vector> +#include "LogMatchingTracker.h" +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +namespace android { +namespace os { +namespace statsd { + +class SimpleLogMatchingTracker : public virtual LogMatchingTracker { +public: + SimpleLogMatchingTracker(const std::string& name, const int index, + const SimpleLogEntryMatcher& matcher); + + ~SimpleLogMatchingTracker(); + + bool init(const std::vector<LogEntryMatcher>& allLogMatchers, + const std::vector<sp<LogMatchingTracker>>& allTrackers, + const std::unordered_map<std::string, int>& matcherMap, + std::vector<bool>& stack) override; + + void onLogEvent(const LogEventWrapper& event, + const std::vector<sp<LogMatchingTracker>>& allTrackers, + std::vector<MatchingState>& matcherResults) override; + +private: + const SimpleLogEntryMatcher mMatcher; +}; + +} // namespace statsd +} // namespace os +} // namespace android +#endif // SIMPLE_LOG_MATCHING_TRACKER_H diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index ab7b2b1dc57b..557c03228436 100644 --- a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -14,26 +14,28 @@ * limitations under the License. */ -#include "LogEntryMatcherManager.h" +#include "matcher_util.h" #include <cutils/log.h> #include <log/event_tag_map.h> #include <log/log_event_list.h> #include <log/logprint.h> #include <utils/Errors.h> #include <unordered_map> +#include "LogMatchingTracker.h" #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -#include "parse_util.h" +#include "stats_util.h" using std::set; using std::string; using std::unordered_map; +using std::vector; namespace android { namespace os { namespace statsd { -LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) { +LogEventWrapper parseLogEvent(log_msg msg) { LogEventWrapper wrapper; wrapper.timestamp_ns = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec; wrapper.tagId = getTagId(msg); @@ -67,7 +69,9 @@ LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) { break; case EVENT_TYPE_STRING: if (index % 2 == 1) { - wrapper.strMap[key] = elem.data.string; + // without explicit calling string() constructor, there will be an + // additional 0 in the end of the string. + wrapper.strMap[key] = string(elem.data.string); } index++; break; @@ -99,57 +103,56 @@ LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) { return wrapper; } -bool LogEntryMatcherManager::matches(const LogEntryMatcher& matcher, const LogEventWrapper& event) { - const int tagId = event.tagId; - const unordered_map<int, long>& intMap = event.intMap; - const unordered_map<int, string>& strMap = event.strMap; - const unordered_map<int, float>& floatMap = event.floatMap; - const unordered_map<int, bool>& boolMap = event.boolMap; - - if (matcher.has_combination()) { // Need to evaluate composite matching - switch (matcher.combination().operation()) { - case LogicalOperation::AND: - for (auto nestedMatcher : matcher.combination().matcher()) { - if (!matches(nestedMatcher, event)) { - return false; // return false if any nested matcher is false; - } +bool combinationMatch(const vector<int>& children, const LogicalOperation& operation, + const vector<MatchingState>& matcherResults) { + bool matched; + switch (operation) { + case LogicalOperation::AND: { + matched = true; + for (const int childIndex : children) { + if (matcherResults[childIndex] != MatchingState::kMatched) { + matched = false; + break; } - return true; // Otherwise, return true. - case LogicalOperation::OR: - for (auto nestedMatcher : matcher.combination().matcher()) { - if (matches(nestedMatcher, event)) { - return true; // return true if any nested matcher is true; - } + } + break; + } + case LogicalOperation::OR: { + matched = false; + for (const int childIndex : children) { + if (matcherResults[childIndex] == MatchingState::kMatched) { + matched = true; + break; } - return false; - case LogicalOperation::NOT: - return !matches(matcher.combination().matcher(0), event); - - // Case NAND is just inverting the return statement of AND - case LogicalOperation::NAND: - for (auto nestedMatcher : matcher.combination().matcher()) { - auto simple = nestedMatcher.simple_log_entry_matcher(); - if (!matches(nestedMatcher, event)) { - return true; // return false if any nested matcher is false; - } + } + break; + } + case LogicalOperation::NOT: + matched = matcherResults[children[0]] == MatchingState::kNotMatched; + break; + case LogicalOperation::NAND: + matched = false; + for (const int childIndex : children) { + if (matcherResults[childIndex] != MatchingState::kMatched) { + matched = true; + break; } - return false; // Otherwise, return true. - case LogicalOperation::NOR: - for (auto nestedMatcher : matcher.combination().matcher()) { - if (matches(nestedMatcher, event)) { - return false; // return true if any nested matcher is true; - } + } + break; + case LogicalOperation::NOR: + matched = true; + for (const int childIndex : children) { + if (matcherResults[childIndex] == MatchingState::kMatched) { + matched = false; + break; } - return true; - } - return false; - } else { - return matchesSimple(matcher.simple_log_entry_matcher(), event); + } + break; } + return matched; } -bool LogEntryMatcherManager::matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, - const LogEventWrapper& event) { +bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEventWrapper& event) { const int tagId = event.tagId; const unordered_map<int, long>& intMap = event.intMap; const unordered_map<int, string>& strMap = event.strMap; @@ -249,26 +252,6 @@ bool LogEntryMatcherManager::matchesSimple(const SimpleLogEntryMatcher& simpleMa return false; } -set<int> LogEntryMatcherManager::getTagIdsFromMatcher(const LogEntryMatcher& matcher) { - set<int> result; - switch (matcher.contents_case()) { - case LogEntryMatcher::kCombination: - for (auto sub_matcher : matcher.combination().matcher()) { - set<int> tagSet = getTagIdsFromMatcher(sub_matcher); - result.insert(tagSet.begin(), tagSet.end()); - } - break; - case LogEntryMatcher::kSimpleLogEntryMatcher: - for (int i = 0; i < matcher.simple_log_entry_matcher().tag_size(); i++) { - result.insert(matcher.simple_log_entry_matcher().tag(i)); - } - break; - case LogEntryMatcher::CONTENTS_NOT_SET: - break; - } - return result; -} - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.h b/cmds/statsd/src/matchers/matcher_util.h index fc8e6a1378a7..6d8e762382f0 100644 --- a/cmds/statsd/src/matchers/LogEntryMatcherManager.h +++ b/cmds/statsd/src/matchers/matcher_util.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef LOG_ENTRY_MATCHER_MANAGER_H -#define LOG_ENTRY_MATCHER_MANAGER_H +#ifndef MATCHER_UTIL_H +#define MATCHER_UTIL_H #include <log/log_read.h> #include <log/logprint.h> @@ -25,9 +25,6 @@ #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" -using std::string; -using std::unordered_map; - namespace android { namespace os { namespace statsd { @@ -41,26 +38,20 @@ typedef struct { std::unordered_map<int, float> floatMap; } LogEventWrapper; -/** - * Keeps track per log entry which simple log entry matchers match. - */ -class LogEntryMatcherManager { -public: - LogEntryMatcherManager(); - - ~LogEntryMatcherManager(){}; - - static LogEventWrapper parseLogEvent(log_msg msg); +enum MatchingState { + kNotComputed = -1, + kNotMatched = 0, + kMatched = 1, +}; - static std::set<int> getTagIdsFromMatcher(const LogEntryMatcher& matcher); +LogEventWrapper parseLogEvent(log_msg msg); - static bool matches(const LogEntryMatcher& matcher, const LogEventWrapper& wrapper); +bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation, + const std::vector<MatchingState>& matcherResults); - static bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, - const LogEventWrapper& wrapper); -}; +bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEventWrapper& wrapper); } // namespace statsd } // namespace os } // namespace android -#endif // LOG_ENTRY_MATCHER_MANAGER_H +#endif // MATCHER_UTIL_H diff --git a/cmds/statsd/src/metrics/ConditionTracker.cpp b/cmds/statsd/src/metrics/ConditionTracker.cpp deleted file mode 100644 index 684ffdb5883a..000000000000 --- a/cmds/statsd/src/metrics/ConditionTracker.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "ConditionTracker" -#define DEBUG true // STOPSHIP if true -#define VLOG(...) \ - if (DEBUG) ALOGD(__VA_ARGS__); - -#include "ConditionTracker.h" -#include <cutils/log.h> - -namespace android { -namespace os { -namespace statsd { - -ConditionTracker::ConditionTracker() : mIsConditionMet(true) { - VLOG("ConditionTracker()"); -} - -ConditionTracker::ConditionTracker(const Condition& condition) - : mCondition(condition), mIsConditionMet(true) { - VLOG("ConditionTracker()"); -} - -ConditionTracker::~ConditionTracker() { - VLOG("~ConditionTracker()"); -} - -void ConditionTracker::evaluateCondition(const LogEventWrapper& event) { - // modify condition. - VLOG("evaluateCondition"); -} - -bool ConditionTracker::isConditionMet() const { - VLOG("isConditionMet() %d", mIsConditionMet); - return mIsConditionMet; -} - -} // namespace statsd -} // namespace os -} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 635777fe2267..e98999e73223 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -21,7 +21,6 @@ #include "CountMetricProducer.h" #include "CountAnomalyTracker.h" -#include "parse_util.h" #include <cutils/log.h> #include <limits.h> @@ -33,15 +32,14 @@ namespace android { namespace os { namespace statsd { -CountMetricProducer::CountMetricProducer(const CountMetric& metric, - const sp<ConditionTracker> condition) +CountMetricProducer::CountMetricProducer(const CountMetric& metric, const bool hasCondition) : mMetric(metric), - mConditionTracker(condition), - mStartTime(std::time(nullptr)), + mStartTime(time(nullptr)), mCounter(0), mCurrentBucketStartTime(mStartTime), // TODO: read mAnomalyTracker parameters from config file. - mAnomalyTracker(6, 10) { + mAnomalyTracker(6, 10), + mCondition(hasCondition ? ConditionState::kUnknown : ConditionState::kTrue) { // TODO: evaluate initial conditions. and set mConditionMet. if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSize_sec = metric.bucket().bucket_size_millis() / 1000; @@ -52,10 +50,6 @@ CountMetricProducer::CountMetricProducer(const CountMetric& metric, VLOG("created. bucket size %lu start_time: %lu", mBucketSize_sec, mStartTime); } -CountMetricProducer::CountMetricProducer(const CountMetric& metric) - : CountMetricProducer(metric, new ConditionTracker()) { -} - CountMetricProducer::~CountMetricProducer() { VLOG("~CountMetricProducer() called"); } @@ -63,13 +57,17 @@ CountMetricProducer::~CountMetricProducer() { void CountMetricProducer::finish() { // TODO: write the StatsLogReport to dropbox using // DropboxWriter. - onDumpReport(); } void CountMetricProducer::onDumpReport() { VLOG("dump report now..."); } +void CountMetricProducer::onConditionChanged(const bool conditionMet) { + VLOG("onConditionChanged"); + mCondition = conditionMet; +} + void CountMetricProducer::onMatchedLogEvent(const LogEventWrapper& event) { time_t eventTime = event.timestamp_ns / 1000000000; @@ -78,22 +76,24 @@ void CountMetricProducer::onMatchedLogEvent(const LogEventWrapper& event) { return; } - if (mConditionTracker->isConditionMet()) { + if (mCondition == ConditionState::kTrue) { flushCounterIfNeeded(eventTime); mCounter++; mAnomalyTracker.checkAnomaly(mCounter); + VLOG("metric %lld count %d", mMetric.metric_id(), mCounter); } } -// When a new matched event comes in, we check if it falls into the current bucket. And flush the -// counter to the StatsLogReport and adjust the bucket if needed. +// When a new matched event comes in, we check if it falls into the current +// bucket. And flush the counter to the StatsLogReport and adjust the bucket if +// needed. void CountMetricProducer::flushCounterIfNeeded(const time_t& eventTime) { if (mCurrentBucketStartTime + mBucketSize_sec > eventTime) { return; } // TODO: add a KeyValuePair to StatsLogReport. - ALOGD("CountMetric: dump counter %d", mCounter); + ALOGD("%lld: dump counter %d", mMetric.metric_id(), mCounter); // adjust the bucket start time time_t numBucketsForward = (eventTime - mCurrentBucketStartTime) @@ -106,7 +106,7 @@ void CountMetricProducer::flushCounterIfNeeded(const time_t& eventTime) { mAnomalyTracker.addPastBucket(mCounter, numBucketsForward); mCounter = 0; - VLOG("new bucket start time: %lu", mCurrentBucketStartTime); + VLOG("%lld: new bucket start time: %lu", mMetric.metric_id(), mCurrentBucketStartTime); } } // namespace statsd diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 7502320410ac..370cd4684458 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -17,40 +17,41 @@ #ifndef COUNT_METRIC_PRODUCER_H #define COUNT_METRIC_PRODUCER_H -#include <mutex> -#include <thread> #include <unordered_map> -#include "../matchers/LogEntryMatcherManager.h" + #include "CountAnomalyTracker.h" -#include "ConditionTracker.h" -#include "DropboxWriter.h" +#include "../condition/ConditionTracker.h" +#include "../matchers/matcher_util.h" #include "MetricProducer.h" #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +using namespace std; + namespace android { namespace os { namespace statsd { class CountMetricProducer : public MetricProducer { public: - CountMetricProducer(const CountMetric& countMetric, const sp<ConditionTracker> condition); - - CountMetricProducer(const CountMetric& countMetric); + CountMetricProducer(const CountMetric& countMetric, const bool hasCondition); virtual ~CountMetricProducer(); void onMatchedLogEvent(const LogEventWrapper& event) override; + void onConditionChanged(const bool conditionMet) override; + void finish() override; void onDumpReport() override; + // TODO: Implement this later. + virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override {}; + private: const CountMetric mMetric; - const sp<ConditionTracker> mConditionTracker; - const time_t mStartTime; // TODO: Add dimensions. // Counter value for the current bucket. @@ -62,6 +63,8 @@ private: CountAnomalyTracker mAnomalyTracker; + bool mCondition; + void flushCounterIfNeeded(const time_t& newEventTime); }; diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 44a778b3d903..b7e965610020 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -18,21 +18,27 @@ #define METRIC_PRODUCER_H #include <log/logprint.h> -#include "../matchers/LogEntryMatcherManager.h" +#include <utils/RefBase.h> +#include "../matchers/matcher_util.h" +#include "PackageInfoListener.h" namespace android { namespace os { namespace statsd { // A MetricProducer is responsible for compute one single metrics, creating stats log report, and -// writing the report to dropbox. -class MetricProducer { +// writing the report to dropbox. MetricProducers should respond to package changes as required in +// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can +// be a no-op. +class MetricProducer : public virtual RefBase, public virtual PackageInfoListener { public: virtual ~MetricProducer(){}; - // Consume the stats log if it's interesting to this metric. + // Consume the parsed stats log entry that already matched the "what" of the metric. virtual void onMatchedLogEvent(const LogEventWrapper& event) = 0; + virtual void onConditionChanged(const bool condition) = 0; + // This is called when the metric collecting is done, e.g., when there is a new configuration // coming. MetricProducer should do the clean up, and dump existing data to dropbox. virtual void finish() = 0; diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index cb7420683380..1e65f5888233 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -21,13 +21,17 @@ #include "MetricsManager.h" #include <cutils/log.h> #include <log/logprint.h> +#include "../condition/CombinationConditionTracker.h" +#include "../condition/SimpleConditionTracker.h" +#include "../matchers/CombinationLogMatchingTracker.h" +#include "../matchers/SimpleLogMatchingTracker.h" #include "CountMetricProducer.h" -#include "parse_util.h" +#include "metrics_manager_util.h" +#include "stats_util.h" using std::make_unique; using std::set; using std::string; -using std::unique_ptr; using std::unordered_map; using std::vector; @@ -35,93 +39,90 @@ namespace android { namespace os { namespace statsd { -MetricsManager::MetricsManager(const StatsdConfig& config) : mConfig(config), mLogMatchers() { - std::unordered_map<string, LogEntryMatcher> matcherMap; - std::unordered_map<string, sp<ConditionTracker>> conditionMap; - - for (int i = 0; i < config.log_entry_matcher_size(); i++) { - const LogEntryMatcher& logMatcher = config.log_entry_matcher(i); - mMatchers.push_back(logMatcher); - - matcherMap[config.log_entry_matcher(i).name()] = logMatcher; - - mLogMatchers[logMatcher.name()] = vector<unique_ptr<MetricProducer>>(); - // Collect all the tag ids that are interesting - set<int> tagIds = LogEntryMatcherManager::getTagIdsFromMatcher(logMatcher); - - mTagIds.insert(tagIds.begin(), tagIds.end()); - } - - for (int i = 0; i < config.condition_size(); i++) { - const Condition& condition = config.condition(i); - conditionMap[condition.name()] = new ConditionTracker(condition); - } - - // Build MetricProducers for each metric defined in config. - // (1) build CountMetricProducer - for (int i = 0; i < config.count_metric_size(); i++) { - const CountMetric& metric = config.count_metric(i); - auto it = mLogMatchers.find(metric.what()); - if (it == mLogMatchers.end()) { - ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str()); - continue; - } - - if (metric.has_condition()) { - auto condition_it = conditionMap.find(metric.condition()); - if (condition_it == conditionMap.end()) { - ALOGW("cannot find the Condition %s in the config", metric.condition().c_str()); - continue; - } - it->second.push_back(make_unique<CountMetricProducer>(metric, condition_it->second)); - } else { - it->second.push_back(make_unique<CountMetricProducer>(metric)); - } - } - - // TODO: build other types of metrics too. +MetricsManager::MetricsManager(const StatsdConfig& config) { + mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers, + mAllMetricProducers, mConditionToMetricMap, mTrackerToMetricMap, + mTrackerToConditionMap); } MetricsManager::~MetricsManager() { VLOG("~MetricManager()"); } +bool MetricsManager::isConfigValid() const { + return mConfigValid; +} + void MetricsManager::finish() { - for (auto const& entryPair : mLogMatchers) { - for (auto const& metric : entryPair.second) { - metric->finish(); - } + for (auto& metricProducer : mAllMetricProducers) { + metricProducer->finish(); } } // Consume the stats log if it's interesting to this metric. void MetricsManager::onLogEvent(const log_msg& logMsg) { + if (!mConfigValid) { + return; + } + int tagId = getTagId(logMsg); if (mTagIds.find(tagId) == mTagIds.end()) { // not interesting... return; } + // Since at least one of the metrics is interested in this event, we parse it now. - LogEventWrapper event = LogEntryMatcherManager::parseLogEvent(logMsg); + LogEventWrapper event = parseLogEvent(logMsg); + vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed); - // Evaluate the conditions. Order matters, this should happen - // before sending the event to metrics - for (auto& condition : mConditionTracker) { - condition->evaluateCondition(event); + for (auto& matcher : mAllLogEntryMatchers) { + matcher->onLogEvent(event, mAllLogEntryMatchers, matcherCache); + } + + // A bitmap to see which ConditionTracker needs to be re-evaluated. + vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false); + + for (const auto& pair : mTrackerToConditionMap) { + if (matcherCache[pair.first] == MatchingState::kMatched) { + const auto& conditionList = pair.second; + for (const int conditionIndex : conditionList) { + conditionToBeEvaluated[conditionIndex] = true; + } + } + } + + vector<ConditionState> conditionCache(mAllConditionTrackers.size(), + ConditionState::kNotEvaluated); + // A bitmap to track if a condition has changed value. + vector<bool> changedCache(mAllConditionTrackers.size(), false); + for (size_t i = 0; i < mAllConditionTrackers.size(); i++) { + if (conditionToBeEvaluated[i] == false) { + continue; + } + + sp<ConditionTracker>& condition = mAllConditionTrackers[i]; + condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache, + changedCache); + if (changedCache[i]) { + auto pair = mConditionToMetricMap.find(i); + if (pair != mConditionToMetricMap.end()) { + auto& metricList = pair->second; + for (auto metricIndex : metricList) { + mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i]); + } + } + } } - // Now find out which LogMatcher matches this event, and let relevant metrics know. - for (auto matcher : mMatchers) { - if (LogEntryMatcherManager::matches(matcher, event)) { - auto it = mLogMatchers.find(matcher.name()); - if (it != mLogMatchers.end()) { - for (auto const& it2 : it->second) { - // Only metrics that matches this event get notified. - it2->onMatchedLogEvent(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) { + auto pair = mTrackerToMetricMap.find(i); + if (pair != mTrackerToMetricMap.end()) { + auto& metricList = pair->second; + for (const int metricIndex : metricList) { + mAllMetricProducers[metricIndex]->onMatchedLogEvent(event); } - } else { - // TODO: we should remove any redundant matchers that the config provides. - ALOGW("Matcher not used by any metrics."); } } } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 77d7535a1ba1..70c34db6b80a 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -20,8 +20,8 @@ #include <cutils/log.h> #include <log/logprint.h> #include <unordered_map> -#include "../matchers/LogEntryMatcherManager.h" -#include "ConditionTracker.h" +#include "../condition/ConditionTracker.h" +#include "../matchers/LogMatchingTracker.h" #include "MetricProducer.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" @@ -36,25 +36,58 @@ public: ~MetricsManager(); - // Consume the stats log if it's interesting to this metric. + // Return whether the configuration is valid. + bool isConfigValid() const; + void onLogEvent(const log_msg& logMsg); + // Called when everything should wrap up. We are about to finish (e.g., new config comes). void finish(); private: - const StatsdConfig mConfig; - // All event tags that are interesting to my metrics. std::set<int> mTagIds; - // The matchers that my metrics share. - std::vector<LogEntryMatcher> mMatchers; + // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in + // MetricManager. There are relationship between them, and the relationship are denoted by index + // instead of poiters. The reasons for this are: (1) the relationship between them are + // complicated, store index instead of pointers reduce the risk of A holds B's sp, and B holds + // A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get the + // related results from a cache using the index. + // TODO: using unique_ptr may be more appriopreate? + + // Hold all the log entry matchers from the config. + std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers; + + // Hold all the conditions from the config. + std::vector<sp<ConditionTracker>> mAllConditionTrackers; + + // Hold all metrics from the config. + std::vector<sp<MetricProducer>> mAllMetricProducers; + + // To make the log processing more efficient, we want to do as much filtering as possible + // before we go into individual trackers and conditions to match. + + // 1st filter: check if the event tag id is in mTagIds. + // 2nd filter: if it is, we parse the event because there is at least one member is interested. + // then pass to all LogMatchingTrackers (itself also filter events by ids). + // 3nd filter: for LogMatchingTrackers that matched this event, we pass this event to the + // ConditionTrackers and MetricProducers that use this matcher. + // 4th filter: for ConditionTrackers that changed value due to this event, we pass + // new conditions to metrics that use this condition. + + // The following map is initialized from the statsd_config. + + // maps from the index of the LogMatchingTracker to index of MetricProducer. + std::unordered_map<int, std::vector<int>> mTrackerToMetricMap; + + // maps from LogMatchingTracker to ConditionTracker + std::unordered_map<int, std::vector<int>> mTrackerToConditionMap; - // The conditions that my metrics share. - std::vector<sp<ConditionTracker>> mConditionTracker; + // maps from ConditionTracker to MetricProducer + std::unordered_map<int, std::vector<int>> mConditionToMetricMap; - // the map from LogEntryMatcher names to the metrics that use this matcher. - std::unordered_map<std::string, std::vector<std::unique_ptr<MetricProducer>>> mLogMatchers; + bool mConfigValid; }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp new file mode 100644 index 000000000000..6fdd228f4910 --- /dev/null +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -0,0 +1,200 @@ +/* + * 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 "../condition/CombinationConditionTracker.h" +#include "../condition/SimpleConditionTracker.h" +#include "../matchers/CombinationLogMatchingTracker.h" +#include "../matchers/SimpleLogMatchingTracker.h" +#include "CountMetricProducer.h" +#include "stats_util.h" + +using std::set; +using std::string; +using std::unordered_map; +using std::vector; + +namespace android { +namespace os { +namespace statsd { + +bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap, + vector<sp<LogMatchingTracker>>& allLogEntryMatchers, set<int>& allTagIds) { + vector<LogEntryMatcher> matcherConfigs; + + for (int i = 0; i < config.log_entry_matcher_size(); i++) { + const LogEntryMatcher& logMatcher = config.log_entry_matcher(i); + + int index = allLogEntryMatchers.size(); + switch (logMatcher.contents_case()) { + case LogEntryMatcher::ContentsCase::kSimpleLogEntryMatcher: + allLogEntryMatchers.push_back(new SimpleLogMatchingTracker( + logMatcher.name(), index, logMatcher.simple_log_entry_matcher())); + break; + case LogEntryMatcher::ContentsCase::kCombination: + allLogEntryMatchers.push_back( + new CombinationLogMatchingTracker(logMatcher.name(), index)); + break; + default: + ALOGE("Matcher %s malformed", logMatcher.name().c_str()); + return false; + // continue; + } + if (logTrackerMap.find(logMatcher.name()) != logTrackerMap.end()) { + ALOGE("Duplicate LogEntryMatcher found!"); + return false; + } + logTrackerMap[logMatcher.name()] = index; + matcherConfigs.push_back(logMatcher); + } + + vector<bool> stackTracker2(allLogEntryMatchers.size(), false); + for (auto& matcher : allLogEntryMatchers) { + if (!matcher->init(matcherConfigs, allLogEntryMatchers, logTrackerMap, stackTracker2)) { + return false; + } + // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. + const set<int>& tagIds = matcher->getTagIds(); + allTagIds.insert(tagIds.begin(), tagIds.end()); + } + return true; +} + +bool initConditions(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) { + vector<Condition> conditionConfigs; + + for (int i = 0; i < config.condition_size(); i++) { + const Condition& condition = config.condition(i); + int index = allConditionTrackers.size(); + switch (condition.contents_case()) { + case Condition::ContentsCase::kSimpleCondition: { + allConditionTrackers.push_back(new SimpleConditionTracker( + condition.name(), index, condition.simple_condition(), logTrackerMap)); + break; + } + case Condition::ContentsCase::kCombination: { + allConditionTrackers.push_back( + new CombinationConditionTracker(condition.name(), index)); + break; + } + default: + ALOGE("Condition %s malformed", condition.name().c_str()); + return false; + } + if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) { + ALOGE("Duplicate Condition found!"); + return false; + } + conditionTrackerMap[condition.name()] = index; + conditionConfigs.push_back(condition); + } + + vector<bool> stackTracker(allConditionTrackers.size(), false); + for (size_t i = 0; i < allConditionTrackers.size(); i++) { + auto& conditionTracker = allConditionTrackers[i]; + if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap, + stackTracker)) { + return false; + } + for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) { + auto& conditionList = trackerToConditionMap[trackerIndex]; + conditionList.push_back(i); + } + } + return true; +} + +bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap, + const unordered_map<string, int>& conditionTrackerMap, + vector<sp<MetricProducer>>& allMetricProducers, + unordered_map<int, std::vector<int>>& conditionToMetricMap, + unordered_map<int, std::vector<int>>& trackerToMetricMap) { + // Build MetricProducers for each metric defined in config. + // (1) build CountMetricProducer + for (int i = 0; i < config.count_metric_size(); i++) { + const CountMetric& metric = config.count_metric(i); + if (!metric.has_what()) { + ALOGW("cannot find what in CountMetric %lld", metric.metric_id()); + return false; + } + + auto logTrackerIt = logTrackerMap.find(metric.what()); + if (logTrackerIt == logTrackerMap.end()) { + ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str()); + return false; + } + + sp<MetricProducer> countProducer; + int metricIndex = allMetricProducers.size(); + if (metric.has_condition()) { + auto condition_it = conditionTrackerMap.find(metric.condition()); + if (condition_it == conditionTrackerMap.end()) { + ALOGW("cannot find the Condition %s in the config", metric.condition().c_str()); + return false; + } + countProducer = new CountMetricProducer(metric, true /*has condition*/); + // will create new vector if not exist before. + auto& metricList = conditionToMetricMap[condition_it->second]; + metricList.push_back(metricIndex); + } else { + countProducer = new CountMetricProducer(metric, false /*no condition*/); + } + + int logTrackerIndex = logTrackerIt->second; + auto& metric_list = trackerToMetricMap[logTrackerIndex]; + metric_list.push_back(metricIndex); + allMetricProducers.push_back(countProducer); + } + + // TODO: build other types of metrics too. + + return true; +} + +bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds, + vector<sp<LogMatchingTracker>>& allLogEntryMatchers, + vector<sp<ConditionTracker>>& allConditionTrackers, + vector<sp<MetricProducer>>& allMetricProducers, + unordered_map<int, std::vector<int>>& conditionToMetricMap, + unordered_map<int, std::vector<int>>& trackerToMetricMap, + unordered_map<int, std::vector<int>>& trackerToConditionMap) { + unordered_map<string, int> logTrackerMap; + unordered_map<string, int> conditionTrackerMap; + + if (!initLogTrackers(config, logTrackerMap, allLogEntryMatchers, allTagIds)) { + ALOGE("initLogMatchingTrackers failed"); + return false; + } + + if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers, + trackerToConditionMap)) { + ALOGE("initConditionTrackers failed"); + return false; + } + + if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allMetricProducers, + conditionToMetricMap, trackerToMetricMap)) { + ALOGE("initMetricProducers failed"); + return false; + } + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h new file mode 100644 index 000000000000..5f1f295d450a --- /dev/null +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -0,0 +1,94 @@ +/* + * 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. + */ +#ifndef METRIC_UTIL_H +#define METRIC_UTIL_H +#include <memory> +#include <set> +#include <unordered_map> +#include <vector> + +#include "../condition/ConditionTracker.h" +#include "../matchers/LogMatchingTracker.h" +#include "CountMetricProducer.h" + +namespace android { +namespace os { +namespace statsd { + +// Helper functions for MetricsManager to initialize from StatsdConfig. +// *Note*: only initStatsdConfig() should be called from outside. +// All other functions are intermediate +// steps, created to make unit tests easier. And most of the parameters in these +// functions are temporary objects in the initialization phase. + +// Initialize the LogMatchingTrackers. +// input: +// [config]: the input StatsdConfig +// output: +// [logTrackerMap]: this map should contain matcher name to index mapping +// [allLogEntryMatchers]: should store the sp to all the LogMatchingTracker +// [allTagIds]: contains the set of all interesting tag ids to this config. +bool initLogTrackers(const StatsdConfig& config, + std::unordered_map<std::string, int>& logTrackerMap, + std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers, + std::set<int>& allTagIds); + +// Initialize ConditionTrackers +// input: +// [config]: the input config +// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. +// output: +// [conditionTrackerMap]: this map should contain condition name to index mapping +// [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, + const std::unordered_map<std::string, int>& logTrackerMap, + std::unordered_map<std::string, int>& conditionTrackerMap, + std::vector<sp<ConditionTracker>>& allConditionTrackers, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap); + +// Initialize MetricProducers. +// input: +// [config]: the input config +// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. +// [conditionTrackerMap]: condition name to index mapping +// output: +// [allMetricProducers]: contains the list of sp to the MetricProducers created. +// [conditionToMetricMap]: contains the mapping from condition tracker index to +// 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 std::unordered_map<std::string, int>& conditionTrackerMap, + std::vector<sp<MetricProducer>>& allMetricProducers, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap); + +// Initialize MetricManager from StatsdConfig. +// Parameters are the members of MetricsManager. See MetricsManager for declaration. +bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds, + std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers, + std::vector<sp<ConditionTracker>>& allConditionTrackers, + std::vector<sp<MetricProducer>>& allMetricProducers, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& trackerToConditionMap); + +} // namespace statsd +} // namespace os +} // namespace android +#endif // METRIC_UTIL_H diff --git a/cmds/statsd/src/parse_util.cpp b/cmds/statsd/src/parse_util.cpp deleted file mode 100644 index 61421880efd6..000000000000 --- a/cmds/statsd/src/parse_util.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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 <log/log_event_list.h> -#include <parse_util.h> - -namespace android { -namespace os { -namespace statsd { - -static inline uint32_t get4LE(const char* src) { - return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); -} - -int getTagId(log_msg msg) { - return get4LE(msg.msg()); -} - -EventMetricData parse(log_msg msg) { - // dump all statsd logs to dropbox for now. - // TODO: Add filtering, aggregation, etc. - EventMetricData eventMetricData; - - // set tag. - int tag = getTagId(msg); - // TODO: Replace the following line when we can serialize on the fly. - //eventMetricData.set_tag(tag); - - // set timestamp of the event. - eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec); - - // start iterating k,v pairs. - android_log_context context = - create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t), - const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t)); - android_log_list_element elem; - - if (context) { - memset(&elem, 0, sizeof(elem)); - size_t index = 0; - int32_t key = -1; - - do { - elem = android_log_read_next(context); - switch ((int)elem.type) { - case EVENT_TYPE_INT: - if (index % 2 == 0) { - key = elem.data.int32; - } else { - // TODO: Fix the following lines when we can serialize on the fly. - /* - int32_t val = elem.data.int32; - KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair(); - keyValuePair->set_key(key); - keyValuePair->set_value_int(val); - */ - } - index++; - break; - case EVENT_TYPE_FLOAT: - if (index % 2 == 1) { - // TODO: Fix the following lines when we can serialize on the fly. - /* - float val = elem.data.float32; - KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair(); - keyValuePair->set_key(key); - keyValuePair->set_value_float(val); - */ - } - index++; - break; - case EVENT_TYPE_STRING: - if (index % 2 == 1) { - // TODO: Fix the following lines when we can serialize on the fly. - /* - char* val = elem.data.string; - KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair(); - keyValuePair->set_key(key); - keyValuePair->set_value_str(val); - */ - } - index++; - break; - case EVENT_TYPE_LONG: - if (index % 2 == 1) { - // TODO: Fix the following lines when we can serialize on the fly. - /* - int64_t val = elem.data.int64; - KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair(); - keyValuePair->set_key(key); - keyValuePair->set_value_int(val); - */ - } - index++; - break; - case EVENT_TYPE_LIST: - break; - case EVENT_TYPE_LIST_STOP: - break; - case EVENT_TYPE_UNKNOWN: - break; - default: - elem.complete = true; - break; - } - - if (elem.complete) { - break; - } - } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete); - - android_log_destroy(&context); - } - - return eventMetricData; -} -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 2dc0cc7b4a6b..6421b70f1f86 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -55,6 +55,43 @@ message CountMetricData { repeated CountBucketInfo bucket_info = 2; } +message DurationBucketInfo { + optional int64 start_bucket_nanos = 1; + + optional int64 end_bucket_nanos = 2; + + optional int64 duration_nanos = 3; +} + +message DurationMetricData { + repeated KeyValuePair dimension = 1; + + repeated DurationBucketInfo bucket_info = 2; +} + +message UidMapping { + message AppInfo { + optional string app = 1; + + optional int32 version = 2; + + optional int32 uid = 3; + } + + repeated AppInfo initial = 1; + + message Change { + optional bool deletion = 1; + + optional int64 timestamp = 2; + optional string app = 3; + optional int32 uid = 4; + + optional int32 version = 5; + } + repeated Change changes = 2; +} + message StatsLogReport { optional int32 metric_id = 1; @@ -68,8 +105,12 @@ message StatsLogReport { message CountMetricDataWrapper { repeated CountMetricData data = 1; } + message DurationMetricDataWrapper { + repeated CountMetricData data = 1; + } oneof data { EventMetricDataWrapper event_metrics = 4; CountMetricDataWrapper count_metrics = 5; + DurationMetricDataWrapper duration_metrics = 6; } } diff --git a/cmds/statsd/src/stats_util.cpp b/cmds/statsd/src/stats_util.cpp new file mode 100644 index 000000000000..978b228891b5 --- /dev/null +++ b/cmds/statsd/src/stats_util.cpp @@ -0,0 +1,289 @@ +/* + * 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 <log/log_event_list.h> +#include "stats_util.h" + +namespace android { +namespace os { +namespace statsd { + +static inline uint32_t get4LE(const char* src) { + return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); +} + +int getTagId(log_msg msg) { + return get4LE(msg.msg()); +} + +EventMetricData parse(log_msg msg) { + // dump all statsd logs to dropbox for now. + // TODO: Add filtering, aggregation, etc. + EventMetricData eventMetricData; + + // set tag. + int tag = getTagId(msg); + // TODO: Replace the following line when we can serialize on the fly. + // eventMetricData.set_tag(tag); + + // set timestamp of the event. + eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec); + + // start iterating k,v pairs. + android_log_context context = + create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t), + const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t)); + android_log_list_element elem; + + if (context) { + memset(&elem, 0, sizeof(elem)); + size_t index = 0; + int32_t key = -1; + + do { + elem = android_log_read_next(context); + switch ((int)elem.type) { + case EVENT_TYPE_INT: + if (index % 2 == 0) { + key = elem.data.int32; + } else { + // TODO: Fix the following lines when we can serialize on the fly. + /* + int32_t val = elem.data.int32; + KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair(); + keyValuePair->set_key(key); + keyValuePair->set_value_int(val); + */ + } + index++; + break; + case EVENT_TYPE_FLOAT: + if (index % 2 == 1) { + // TODO: Fix the following lines when we can serialize on the fly. + /* + float val = elem.data.float32; + KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair(); + keyValuePair->set_key(key); + keyValuePair->set_value_float(val); + */ + } + index++; + break; + case EVENT_TYPE_STRING: + if (index % 2 == 1) { + // TODO: Fix the following lines when we can serialize on the fly. + /* + char* val = elem.data.string; + KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair(); + keyValuePair->set_key(key); + keyValuePair->set_value_str(val); + */ + } + index++; + break; + case EVENT_TYPE_LONG: + if (index % 2 == 1) { + // TODO: Fix the following lines when we can serialize on the fly. + /* + int64_t val = elem.data.int64; + KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair(); + keyValuePair->set_key(key); + keyValuePair->set_value_int(val); + */ + } + index++; + break; + case EVENT_TYPE_LIST: + break; + case EVENT_TYPE_LIST_STOP: + break; + case EVENT_TYPE_UNKNOWN: + break; + default: + elem.complete = true; + break; + } + + if (elem.complete) { + break; + } + } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete); + + android_log_destroy(&context); + } + + return eventMetricData; +} + +StatsdConfig buildFakeConfig() { + // HACK: Hard code a test metric for counting screen on events... + StatsdConfig config; + config.set_config_id(12345L); + + // One count metric to count screen on + CountMetric* metric = config.add_count_metric(); + metric->set_metric_id(20150717L); + metric->set_what("SCREEN_IS_ON"); + metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); + + // One count metric to count PHOTO_CHANGE_OR_CHROME_CRASH + metric = config.add_count_metric(); + metric->set_metric_id(20150718L); + metric->set_what("PHOTO_PROCESS_STATE_CHANGE"); + metric->mutable_bucket()->set_bucket_size_millis(60 * 1000L); + metric->set_condition("SCREEN_IS_ON"); + + + LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_ON"); + + SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + + + eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_OFF"); + + simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); + + + + LogEntryMatcher* procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("PHOTO_CRASH"); + + SimpleLogEntryMatcher* simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); + simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); + KeyValueMatcher* keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key( + 1002 /*pkg*/); + keyValueMatcher->set_eq_string( + "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + keyValueMatcher->set_eq_int(2); + + + procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("PHOTO_START"); + + simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); + simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key( + 1002 /*pkg*/); + keyValueMatcher->set_eq_string( + "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key( + 1 /*STATE*/); + keyValueMatcher->set_eq_int(1); + + + procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("PHOTO_PROCESS_STATE_CHANGE"); + LogEntryMatcher_Combination* combinationMatcher = procEventMatcher->mutable_combination(); + combinationMatcher->set_operation(LogicalOperation::OR); + combinationMatcher->add_matcher("PHOTO_START"); + combinationMatcher->add_matcher("PHOTO_CRASH"); + + + procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("CHROME_CRASH"); + + simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher(); + simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/); + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key( + 1002 /*pkg*/); + keyValueMatcher->set_eq_string( + "com.android.chrome" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + keyValueMatcher = simpleLogMatcher2->add_key_value_matcher(); + keyValueMatcher->mutable_key_matcher()->set_key( + 1 /*STATE*/); + keyValueMatcher->set_eq_int(2); + + + + procEventMatcher = config.add_log_entry_matcher(); + procEventMatcher->set_name("PHOTO_CHANGE_OR_CHROME_CRASH"); + combinationMatcher = procEventMatcher->mutable_combination(); + combinationMatcher->set_operation(LogicalOperation::OR); + combinationMatcher->add_matcher("PHOTO_PROCESS_STATE_CHANGE"); + combinationMatcher->add_matcher("CHROME_CRASH"); + + + + Condition* condition = config.add_condition(); + condition->set_name("SCREEN_IS_ON"); + SimpleCondition* simpleCondition = condition->mutable_simple_condition(); + simpleCondition->set_start("SCREEN_IS_ON"); + simpleCondition->set_stop("SCREEN_IS_OFF"); + + + condition = config.add_condition(); + condition->set_name("PHOTO_STARTED"); + + simpleCondition = condition->mutable_simple_condition(); + simpleCondition->set_start("PHOTO_START"); + simpleCondition->set_stop("PHOTO_CRASH"); + + + condition = config.add_condition(); + condition->set_name("SCREEN_IS_OFF"); + + simpleCondition = condition->mutable_simple_condition(); + simpleCondition->set_start("SCREEN_IS_OFF"); + simpleCondition->set_stop("SCREEN_IS_ON"); + + + condition = config.add_condition(); + condition->set_name("SCREEN_IS_EITHER_ON_OFF"); + + Condition_Combination* combination = condition->mutable_combination(); + combination->set_operation(LogicalOperation::OR); + combination->add_condition("SCREEN_IS_ON"); + combination->add_condition("SCREEN_IS_OFF"); + + + condition = config.add_condition(); + condition->set_name("SCREEN_IS_NEITHER_ON_OFF"); + + combination = condition->mutable_combination(); + combination->set_operation(LogicalOperation::NOR); + combination->add_condition("SCREEN_IS_ON"); + combination->add_condition("SCREEN_IS_OFF"); + + return config; +} + + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/parse_util.h b/cmds/statsd/src/stats_util.h index 8b82e7bc15a7..25b9bba56280 100644 --- a/cmds/statsd/src/parse_util.h +++ b/cmds/statsd/src/stats_util.h @@ -20,6 +20,7 @@ #include "LogReader.h" #include <log/logprint.h> +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" namespace android { namespace os { @@ -29,6 +30,8 @@ EventMetricData parse(log_msg msg); int getTagId(log_msg msg); +StatsdConfig buildFakeConfig(); + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 3e4ebaf06268..d7702cdd42ad 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -65,7 +65,8 @@ message LogEntryMatcher { message Combination { optional LogicalOperation operation = 1; - repeated LogEntryMatcher matcher = 2; + + repeated string matcher = 2; } oneof contents { SimpleLogEntryMatcher simple_log_entry_matcher = 2; @@ -122,6 +123,24 @@ message CountMetric { optional Bucket bucket = 5; } +message DurationMetric { + optional int64 metric_id = 1; + + enum AggregationType { + DURATION_SUM = 1; + + DURATION_MAX_SPARSE = 2; + DURATION_MIN_SPARSE = 3; + } + optional AggregationType type = 2; + + optional string predicate = 3; + + repeated KeyMatcher dimension = 4; + + optional Bucket bucket = 5; +} + message StatsdConfig { optional int64 config_id = 1; diff --git a/cmds/statsd/tests/ConditionTracker_test.cpp b/cmds/statsd/tests/ConditionTracker_test.cpp new file mode 100644 index 000000000000..f8b0fd0796e7 --- /dev/null +++ b/cmds/statsd/tests/ConditionTracker_test.cpp @@ -0,0 +1,162 @@ +// 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. + +#define LOG_TAG "statsd_test" + +#include <gtest/gtest.h> +#include "../src/condition/condition_util.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +#include <stdio.h> +#include <vector> + +using namespace android::os::statsd; +using std::vector; + + +#ifdef __ANDROID__ +TEST(ConditionTrackerTest, TestUnknownCondition) { + LogicalOperation operation = LogicalOperation::AND; + + vector<int> children; + children.push_back(0); + children.push_back(1); + children.push_back(2); + + vector<ConditionState> conditionResults; + conditionResults.push_back(ConditionState::kUnknown); + conditionResults.push_back(ConditionState::kFalse); + conditionResults.push_back(ConditionState::kTrue); + + EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults), + ConditionState::kUnknown); +} +TEST(ConditionTrackerTest, TestAndCondition) { + // Set up the matcher + LogicalOperation operation = LogicalOperation::AND; + + vector<int> children; + children.push_back(0); + children.push_back(1); + children.push_back(2); + + vector<ConditionState> conditionResults; + conditionResults.push_back(ConditionState::kTrue); + conditionResults.push_back(ConditionState::kFalse); + conditionResults.push_back(ConditionState::kTrue); + + EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); + + conditionResults.clear(); + conditionResults.push_back(ConditionState::kTrue); + conditionResults.push_back(ConditionState::kTrue); + conditionResults.push_back(ConditionState::kTrue); + + EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); +} + +TEST(ConditionTrackerTest, TestOrCondition) { + // Set up the matcher + LogicalOperation operation = LogicalOperation::OR; + + vector<int> children; + children.push_back(0); + children.push_back(1); + children.push_back(2); + + vector<ConditionState> conditionResults; + conditionResults.push_back(ConditionState::kTrue); + conditionResults.push_back(ConditionState::kFalse); + conditionResults.push_back(ConditionState::kTrue); + + EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); + + conditionResults.clear(); + conditionResults.push_back(ConditionState::kFalse); + conditionResults.push_back(ConditionState::kFalse); + conditionResults.push_back(ConditionState::kFalse); + + EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); +} + +TEST(ConditionTrackerTest, TestNotCondition) { + // Set up the matcher + LogicalOperation operation = LogicalOperation::NOT; + + vector<int> children; + children.push_back(0); + + vector<ConditionState> conditionResults; + conditionResults.push_back(ConditionState::kTrue); + + EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); + + conditionResults.clear(); + conditionResults.push_back(ConditionState::kFalse); + EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); +} + +TEST(ConditionTrackerTest, TestNandCondition) { + // Set up the matcher + LogicalOperation operation = LogicalOperation::NAND; + + vector<int> children; + children.push_back(0); + children.push_back(1); + + vector<ConditionState> conditionResults; + conditionResults.push_back(ConditionState::kTrue); + conditionResults.push_back(ConditionState::kFalse); + + EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); + + conditionResults.clear(); + conditionResults.push_back(ConditionState::kFalse); + conditionResults.push_back(ConditionState::kFalse); + EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); + + conditionResults.clear(); + conditionResults.push_back(ConditionState::kTrue); + conditionResults.push_back(ConditionState::kTrue); + EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); +} + +TEST(ConditionTrackerTest, TestNorCondition) { + // Set up the matcher + LogicalOperation operation = LogicalOperation::NOR; + + vector<int> children; + children.push_back(0); + children.push_back(1); + + vector<ConditionState> conditionResults; + conditionResults.push_back(ConditionState::kTrue); + conditionResults.push_back(ConditionState::kFalse); + + EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); + + conditionResults.clear(); + conditionResults.push_back(ConditionState::kFalse); + conditionResults.push_back(ConditionState::kFalse); + EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults)); + + conditionResults.clear(); + conditionResults.push_back(ConditionState::kTrue); + conditionResults.push_back(ConditionState::kTrue); + EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults)); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 473704a7e7b9..606980178e2a 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -18,14 +18,15 @@ #include <log/log_event_list.h> #include <log/log_read.h> #include <log/logprint.h> -#include "../src/matchers/LogEntryMatcherManager.h" -#include "../src/parse_util.h" +#include "../src/matchers/matcher_util.h" +#include "../src/stats_util.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include <stdio.h> using namespace android::os::statsd; using std::unordered_map; +using std::vector; const int kTagIdWakelock = 123; const int kKeyIdState = 45; @@ -41,7 +42,7 @@ TEST(LogEntryMatcherTest, TestSimpleMatcher) { LogEventWrapper wrapper; wrapper.tagId = kTagIdWakelock; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); } TEST(LogEntryMatcherTest, TestBoolMatcher) { @@ -57,13 +58,13 @@ TEST(LogEntryMatcherTest, TestBoolMatcher) { keyValue->set_eq_bool(true); wrapper.boolMap[kKeyIdState] = true; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); keyValue->set_eq_bool(false); - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); - wrapper.boolMap[kTagIdWakelock] = false; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + wrapper.boolMap[kKeyIdState] = false; + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); } TEST(LogEntryMatcherTest, TestStringMatcher) { @@ -80,7 +81,7 @@ TEST(LogEntryMatcherTest, TestStringMatcher) { wrapper.strMap[kKeyIdState] = "wakelock_name"; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); } TEST(LogEntryMatcherTest, TestIntComparisonMatcher) { @@ -96,19 +97,19 @@ TEST(LogEntryMatcherTest, TestIntComparisonMatcher) { keyValue->set_lt_int(10); wrapper.intMap[kKeyIdState] = 11; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); wrapper.intMap[kKeyIdState] = 10; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); wrapper.intMap[kKeyIdState] = 9; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); keyValue->set_gt_int(10); wrapper.intMap[kKeyIdState] = 11; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); wrapper.intMap[kKeyIdState] = 10; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); wrapper.intMap[kKeyIdState] = 9; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); } TEST(LogEntryMatcherTest, TestIntWithEqualityComparisonMatcher) { @@ -124,19 +125,19 @@ TEST(LogEntryMatcherTest, TestIntWithEqualityComparisonMatcher) { keyValue->set_lte_int(10); wrapper.intMap[kKeyIdState] = 11; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); wrapper.intMap[kKeyIdState] = 10; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); wrapper.intMap[kKeyIdState] = 9; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); keyValue->set_gte_int(10); wrapper.intMap[kKeyIdState] = 11; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); wrapper.intMap[kKeyIdState] = 10; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); wrapper.intMap[kKeyIdState] = 9; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); } TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) { @@ -152,15 +153,15 @@ TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) { keyValue->set_lt_float(10.0); wrapper.floatMap[kKeyIdState] = 10.1; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); wrapper.floatMap[kKeyIdState] = 9.9; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); keyValue->set_gt_float(10.0); wrapper.floatMap[kKeyIdState] = 10.1; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper)); wrapper.floatMap[kKeyIdState] = 9.9; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper)); } // Helper for the composite matchers. @@ -173,141 +174,117 @@ void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, int tag, int key, in TEST(LogEntryMatcherTest, TestAndMatcher) { // Set up the matcher - LogEntryMatcher matcher; - auto combination = matcher.mutable_combination(); - combination->set_operation(LogicalOperation::AND); + LogicalOperation operation = LogicalOperation::AND; - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdState, 3); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdPackageVersion, 4); + vector<int> children; + children.push_back(0); + children.push_back(1); + children.push_back(2); - LogEventWrapper wrapper; - wrapper.tagId = kTagIdWakelock; + vector<MatchingState> matcherResults; + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kNotMatched); + matcherResults.push_back(MatchingState::kMatched); + + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - wrapper.intMap[1003] = 4; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap.clear(); - wrapper.intMap[1] = 3; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap.clear(); - wrapper.intMap[1] = 3; - wrapper.intMap[1003] = 4; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + matcherResults.clear(); + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kMatched); + + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); } TEST(LogEntryMatcherTest, TestOrMatcher) { // Set up the matcher - LogEntryMatcher matcher; - auto combination = matcher.mutable_combination(); - combination->set_operation(LogicalOperation::OR); + LogicalOperation operation = LogicalOperation::OR; - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdState, 3); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdPackageVersion, 4); + vector<int> children; + children.push_back(0); + children.push_back(1); + children.push_back(2); - LogEventWrapper wrapper; - wrapper.tagId = kTagIdWakelock; + vector<MatchingState> matcherResults; + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kNotMatched); + matcherResults.push_back(MatchingState::kMatched); + + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - // Don't set any key-value pairs. - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap[1003] = 4; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap.clear(); - wrapper.intMap[1] = 3; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap.clear(); - wrapper.intMap[1] = 3; - wrapper.intMap[1003] = 4; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); + matcherResults.clear(); + matcherResults.push_back(MatchingState::kNotMatched); + matcherResults.push_back(MatchingState::kNotMatched); + matcherResults.push_back(MatchingState::kNotMatched); + + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); } TEST(LogEntryMatcherTest, TestNotMatcher) { // Set up the matcher - LogEntryMatcher matcher; - auto combination = matcher.mutable_combination(); - combination->set_operation(LogicalOperation::NOT); + LogicalOperation operation = LogicalOperation::NOT; - // Define first simpleMatcher - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdState, 3); + vector<int> children; + children.push_back(0); - LogEventWrapper wrapper; - wrapper.tagId = kTagIdWakelock; + vector<MatchingState> matcherResults; + matcherResults.push_back(MatchingState::kMatched); + + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - // Don't set any key-value pairs. - wrapper.intMap[kKeyIdState] = 3; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + matcherResults.clear(); + matcherResults.push_back(MatchingState::kNotMatched); + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); } -TEST(LogEntryMatcherTest, TestNANDMatcher) { +TEST(LogEntryMatcherTest, TestNandMatcher) { // Set up the matcher - LogEntryMatcher matcher; - auto combination = matcher.mutable_combination(); - combination->set_operation(LogicalOperation::NAND); + LogicalOperation operation = LogicalOperation::NAND; - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdState, 3); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdPackageVersion, 4); + vector<int> children; + children.push_back(0); + children.push_back(1); - LogEventWrapper wrapper; - wrapper.tagId = kTagIdWakelock; + vector<MatchingState> matcherResults; + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kNotMatched); - // Don't set any key-value pairs. - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap[kKeyIdState] = 3; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap[kKeyIdPackageVersion] = 4; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); -} + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); -TEST(LogEntryMatcherTest, TestNORMatcher) { - // Set up the matcher - LogEntryMatcher matcher; - auto combination = matcher.mutable_combination(); - combination->set_operation(LogicalOperation::NOR); + matcherResults.clear(); + matcherResults.push_back(MatchingState::kNotMatched); + matcherResults.push_back(MatchingState::kNotMatched); + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdState, 3); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdPackageVersion, 4); + matcherResults.clear(); + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kMatched); + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); +} - LogEventWrapper wrapper; - wrapper.tagId = kTagIdWakelock; +TEST(LogEntryMatcherTest, TestNorMatcher) { + // Set up the matcher + LogicalOperation operation = LogicalOperation::NOR; - // Don't set any key-value pairs. - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap[kKeyIdState] = 3; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap[kKeyIdPackageVersion] = 4; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); -} + vector<int> children; + children.push_back(0); + children.push_back(1); -// Tests that a NOT on top of AND is the same as NAND -TEST(LogEntryMatcherTest, TestMultipleLayerMatcher) { - LogEntryMatcher matcher; - auto not_combination = matcher.mutable_combination(); - not_combination->set_operation(LogicalOperation::NOT); + vector<MatchingState> matcherResults; + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kNotMatched); - // Now add the AND - auto combination = not_combination->add_matcher()->mutable_combination(); - combination->set_operation(LogicalOperation::AND); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdState, 3); - addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), - kTagIdWakelock, kKeyIdPackageVersion, 4); + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); - LogEventWrapper wrapper; - wrapper.tagId = kTagIdWakelock; + matcherResults.clear(); + matcherResults.push_back(MatchingState::kNotMatched); + matcherResults.push_back(MatchingState::kNotMatched); + EXPECT_TRUE(combinationMatch(children, operation, matcherResults)); - // Don't set any key-value pairs. - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap[kKeyIdState] = 3; - EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper)); - wrapper.intMap[kKeyIdPackageVersion] = 4; - EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper)); + matcherResults.clear(); + matcherResults.push_back(MatchingState::kMatched); + matcherResults.push_back(MatchingState::kMatched); + EXPECT_FALSE(combinationMatch(children, operation, matcherResults)); } #else diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp new file mode 100644 index 000000000000..673c15686f71 --- /dev/null +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -0,0 +1,231 @@ +// 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. + +#define LOG_TAG "statsd_test" + +#include <gtest/gtest.h> +#include "../src/condition/ConditionTracker.h" +#include "../src/matchers/LogMatchingTracker.h" +#include "../src/metrics/CountMetricProducer.h" +#include "../src/metrics/MetricProducer.h" +#include "../src/metrics/metrics_manager_util.h" + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +#include <stdio.h> +#include <set> +#include <unordered_map> +#include <vector> + +using namespace android::os::statsd; +using android::sp; +using std::set; +using std::unordered_map; +using std::vector; + +#ifdef __ANDROID__ + +// TODO: ADD MORE TEST CASES. + +StatsdConfig buildGoodConfig() { + StatsdConfig config; + config.set_config_id(12345L); + + LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_ON"); + + SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_OFF"); + + simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); + + eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_ON_OR_OFF"); + + LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination(); + combination->set_operation(LogicalOperation::OR); + combination->add_matcher("SCREEN_IS_ON"); + combination->add_matcher("SCREEN_IS_OFF"); + + return config; +} + +StatsdConfig buildCircleMatchers() { + StatsdConfig config; + config.set_config_id(12345L); + + LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_ON"); + + SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_ON_OR_OFF"); + + LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination(); + combination->set_operation(LogicalOperation::OR); + combination->add_matcher("SCREEN_IS_ON"); + // Circle dependency + combination->add_matcher("SCREEN_ON_OR_OFF"); + + return config; +} + +StatsdConfig buildMissingMatchers() { + StatsdConfig config; + config.set_config_id(12345L); + + LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_ON"); + + SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_ON_OR_OFF"); + + LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination(); + combination->set_operation(LogicalOperation::OR); + combination->add_matcher("SCREEN_IS_ON"); + // undefined matcher + combination->add_matcher("ABC"); + + return config; +} + +StatsdConfig buildCircleConditions() { + StatsdConfig config; + config.set_config_id(12345L); + + LogEntryMatcher* eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_ON"); + + SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); + + eventMatcher = config.add_log_entry_matcher(); + eventMatcher->set_name("SCREEN_IS_OFF"); + + simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher(); + simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/); + simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); + simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int( + 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); + + Condition* condition = config.add_condition(); + condition->set_name("SCREEN_IS_ON"); + SimpleCondition* simpleCondition = condition->mutable_simple_condition(); + simpleCondition->set_start("SCREEN_IS_ON"); + simpleCondition->set_stop("SCREEN_IS_OFF"); + + condition = config.add_condition(); + condition->set_name("SCREEN_IS_EITHER_ON_OFF"); + + Condition_Combination* combination = condition->mutable_combination(); + combination->set_operation(LogicalOperation::OR); + combination->add_condition("SCREEN_IS_ON"); + combination->add_condition("SCREEN_IS_EITHER_ON_OFF"); + + return config; +} + +TEST(MetricsManagerTest, TestGoodConfig) { + StatsdConfig config = buildGoodConfig(); + set<int> allTagIds; + vector<sp<LogMatchingTracker>> allLogEntryMatchers; + vector<sp<ConditionTracker>> allConditionTrackers; + vector<sp<MetricProducer>> allMetricProducers; + unordered_map<int, std::vector<int>> conditionToMetricMap; + unordered_map<int, std::vector<int>> trackerToMetricMap; + unordered_map<int, std::vector<int>> trackerToConditionMap; + + EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers, + allMetricProducers, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap)); +} + +TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { + StatsdConfig config = buildCircleMatchers(); + set<int> allTagIds; + vector<sp<LogMatchingTracker>> allLogEntryMatchers; + vector<sp<ConditionTracker>> allConditionTrackers; + vector<sp<MetricProducer>> allMetricProducers; + 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, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap)); +} + +TEST(MetricsManagerTest, TestMissingMatchers) { + StatsdConfig config = buildMissingMatchers(); + set<int> allTagIds; + vector<sp<LogMatchingTracker>> allLogEntryMatchers; + vector<sp<ConditionTracker>> allConditionTrackers; + vector<sp<MetricProducer>> allMetricProducers; + 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, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap)); +} + +TEST(MetricsManagerTest, TestCircleConditionDependency) { + StatsdConfig config = buildCircleConditions(); + set<int> allTagIds; + vector<sp<LogMatchingTracker>> allLogEntryMatchers; + vector<sp<ConditionTracker>> allConditionTrackers; + vector<sp<MetricProducer>> allMetricProducers; + 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, conditionToMetricMap, trackerToMetricMap, + trackerToConditionMap)); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp new file mode 100644 index 000000000000..b6f14493cb36 --- /dev/null +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -0,0 +1,69 @@ +// 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. + +#define LOG_TAG "statsd_test" + +#include <gtest/gtest.h> +#include "../src/UidMap.h" +#include <stdio.h> + +using namespace android; +using namespace android::os::statsd; + +#ifdef __ANDROID__ +const string kApp1 = "app1.sharing.1"; +const string kApp2 = "app2.sharing.1"; + +TEST(UidMapTest, TestMatching) { + UidMap m; + vector<int32_t> uids; + vector<int32_t> versions; + vector<String16> apps; + + uids.push_back(1000); + uids.push_back(1000); + apps.push_back(String16(kApp1.c_str())); + apps.push_back(String16(kApp2.c_str())); + versions.push_back(4); + versions.push_back(5); + m.updateMap(uids, versions, apps); + EXPECT_TRUE(m.hasApp(1000, kApp1)); + EXPECT_TRUE(m.hasApp(1000, kApp2)); + EXPECT_FALSE(m.hasApp(1000, "not.app")); +} + +TEST(UidMapTest, TestAddAndRemove) { + UidMap m; + vector<int32_t> uids; + vector<int32_t> versions; + vector<String16> apps; + + uids.push_back(1000); + uids.push_back(1000); + apps.push_back(String16(kApp1.c_str())); + apps.push_back(String16(kApp2.c_str())); + versions.push_back(4); + versions.push_back(5); + m.updateMap(uids, versions, apps); + + m.updateApp(String16(kApp1.c_str()), 1000, 40); + EXPECT_EQ(40, m.getAppVersion(1000, kApp1)); + + m.removeApp(String16(kApp1.c_str()), 1000); + EXPECT_FALSE(m.hasApp(1000, kApp1)); + EXPECT_TRUE(m.hasApp(1000, kApp2)); +} +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif
\ No newline at end of file diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 8987bc0289b7..23c4166da104 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -73,15 +73,16 @@ public class StatusBarManager { public static final int DISABLE2_QUICK_SETTINGS = 1; public static final int DISABLE2_SYSTEM_ICONS = 1 << 1; public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2; + public static final int DISABLE2_GLOBAL_ACTIONS = 1 << 3; public static final int DISABLE2_NONE = 0x00000000; public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS - | DISABLE2_NOTIFICATION_SHADE; + | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS; @IntDef(flag = true, value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS, - DISABLE2_NOTIFICATION_SHADE}) + DISABLE2_NOTIFICATION_SHADE, DISABLE2_GLOBAL_ACTIONS}) @Retention(RetentionPolicy.SOURCE) public @interface Disable2Flags {} diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java index 3868439f092f..0deb2e13dce0 100644 --- a/core/java/android/app/job/JobScheduler.java +++ b/core/java/android/app/job/JobScheduler.java @@ -24,7 +24,6 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.ClipData; import android.content.Context; -import android.content.Intent; import android.os.Bundle; import android.os.PersistableBundle; @@ -40,16 +39,18 @@ import java.util.List; * and how to construct them. You will construct these JobInfo objects and pass them to the * JobScheduler with {@link #schedule(JobInfo)}. When the criteria declared are met, the * system will execute this job on your application's {@link android.app.job.JobService}. - * You identify which JobService is meant to execute the logic for your job when you create the - * JobInfo with + * You identify the service component that implements the logic for your job when you + * construct the JobInfo using * {@link android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)}. * </p> * <p> - * The framework will be intelligent about when you receive your callbacks, and attempt to batch - * and defer them as much as possible. Typically if you don't specify a deadline on your job, it - * can be run at any moment depending on the current state of the JobScheduler's internal queue, - * however it might be deferred as long as until the next time the device is connected to a power - * source. + * The framework will be intelligent about when it executes jobs, and attempt to batch + * and defer them as much as possible. Typically if you don't specify a deadline on a job, it + * can be run at any moment depending on the current state of the JobScheduler's internal queue. + * <p> + * While a job is running, the system holds a wakelock on behalf of your app. For this reason, + * you do not need to take any action to guarantee that the device stays awake for the + * duration of the job. * </p> * <p>You do not * instantiate this class directly; instead, retrieve it through @@ -141,30 +142,34 @@ public abstract class JobScheduler { int userId, String tag); /** - * Cancel a job that is pending in the JobScheduler. - * @param jobId unique identifier for this job. Obtain this value from the jobs returned by - * {@link #getAllPendingJobs()}. + * Cancel the specified job. If the job is currently executing, it is stopped + * immediately and the return value from its {@link JobService#onStopJob(JobParameters)} + * method is ignored. + * + * @param jobId unique identifier for the job to be canceled, as supplied to + * {@link JobInfo.Builder#JobInfo.Builder(int, android.content.ComponentName) + * JobInfo.Builder(int, android.content.ComponentName)}. */ public abstract void cancel(int jobId); /** - * Cancel all jobs that have been registered with the JobScheduler by this package. + * Cancel <em>all</em> jobs that have been scheduled by the calling application. */ public abstract void cancelAll(); /** - * Retrieve all jobs for this package that are pending in the JobScheduler. + * Retrieve all jobs that have been scheduled by the calling application. * - * @return a list of all the jobs registered by this package that have not - * yet been executed. + * @return a list of all of the app's scheduled jobs. This includes jobs that are + * currently started as well as those that are still waiting to run. */ public abstract @NonNull List<JobInfo> getAllPendingJobs(); /** - * Retrieve a specific job for this package that is pending in the - * JobScheduler. + * Look up the description of a scheduled job. * - * @return job registered by this package that has not yet been executed. + * @return The {@link JobInfo} description of the given scheduled job, or {@code null} + * if the supplied job ID does not correspond to any job. */ public abstract @Nullable JobInfo getPendingJob(int jobId); } diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java index 9096b47b8d4d..69afed205de1 100644 --- a/core/java/android/app/job/JobService.java +++ b/core/java/android/app/job/JobService.java @@ -18,16 +18,7 @@ package android.app.job; import android.app.Service; import android.content.Intent; -import android.os.Handler; import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; - -import java.lang.ref.WeakReference; /** * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p> @@ -55,7 +46,7 @@ public abstract class JobService extends Service { * </pre> * * <p>If a job service is declared in the manifest but not protected with this - * permission, that service will be ignored by the OS. + * permission, that service will be ignored by the system. */ public static final String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE"; @@ -81,14 +72,36 @@ public abstract class JobService extends Service { } /** - * Override this method with the callback logic for your job. Any such logic needs to be - * performed on a separate thread, as this function is executed on your application's main - * thread. + * Called to indicate that the job has begun executing. Override this method with the + * logic for your job. Like all other component lifecycle callbacks, this method executes + * on your application's main thread. + * <p> + * Return {@code true} from this method if your job needs to continue running. If you + * do this, the job remains active until you call + * {@link #jobFinished(JobParameters, boolean)} to tell the system that it has completed + * its work, or until the job's required constraints are no longer satisfied. For + * example, if the job was scheduled using + * {@link JobInfo.Builder#setRequiresCharging(boolean) setRequiresCharging(true)}, + * it will be immediately halted by the system if the user unplugs the device from power, + * the job's {@link #onStopJob(JobParameters)} callback will be invoked, and the app + * will be expected to shut down all ongoing work connected with that job. + * <p> + * The system holds a wakelock on behalf of your app as long as your job is executing. + * This wakelock is acquired before this method is invoked, and is not released until either + * you call {@link #jobFinished(JobParameters, boolean)}, or after the system invokes + * {@link #onStopJob(JobParameters)} to notify your job that it is being shut down + * prematurely. + * <p> + * Returning {@code false} from this method means your job is already finished. The + * system's wakelock for the job will be released, and {@link #onStopJob(JobParameters)} + * will not be invoked. * - * @param params Parameters specifying info about this job, including the extras bundle you - * optionally provided at job-creation time. - * @return True if your service needs to process the work (on a separate thread). False if - * there's no more work to be done for this job. + * @param params Parameters specifying info about this job, including the optional + * extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle). + * This object serves to identify this specific running job instance when calling + * {@link #jobFinished(JobParameters, boolean)}. + * @return {@code true} if your service will continue running, using a separate thread + * when appropriate. {@code false} means that this job has completed its work. */ public abstract boolean onStartJob(JobParameters params); @@ -101,37 +114,44 @@ public abstract class JobService extends Service { * {@link android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}, yet while your * job was executing the user toggled WiFi. Another example is if you had specified * {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its - * idle maintenance window. You are solely responsible for the behaviour of your application - * upon receipt of this message; your app will likely start to misbehave if you ignore it. One - * immediate repercussion is that the system will cease holding a wakelock for you.</p> + * idle maintenance window. You are solely responsible for the behavior of your application + * upon receipt of this message; your app will likely start to misbehave if you ignore it. + * <p> + * Once this method returns, the system releases the wakelock that it is holding on + * behalf of the job.</p> * - * @param params Parameters specifying info about this job. - * @return True to indicate to the JobManager whether you'd like to reschedule this job based - * on the retry criteria provided at job creation-time. False to drop the job. Regardless of - * the value returned, your job must stop executing. + * @param params The parameters identifying this job, as supplied to + * the job in the {@link #onStartJob(JobParameters)} callback. + * @return {@code true} to indicate to the JobManager whether you'd like to reschedule + * this job based on the retry criteria provided at job creation-time; or {@code false} + * to end the job entirely. Regardless of the value returned, your job must stop executing. */ public abstract boolean onStopJob(JobParameters params); /** - * Call this to inform the JobManager you've finished executing. This can be called from any - * thread, as it will ultimately be run on your application's main thread. When the system - * receives this message it will release the wakelock being held. + * Call this to inform the JobScheduler that the job has finished its work. When the + * system receives this message, it releases the wakelock being held for the job. * <p> - * You can specify post-execution behaviour to the scheduler here with - * <code>needsReschedule </code>. This will apply a back-off timer to your job based on - * the default, or what was set with - * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}. The original - * requirements are always honoured even for a backed-off job. Note that a job running in - * idle mode will not be backed-off. Instead what will happen is the job will be re-added - * to the queue and re-executed within a future idle maintenance window. + * You can request that the job be scheduled again by passing {@code true} as + * the <code>wantsReschedule</code> parameter. This will apply back-off policy + * for the job; this policy can be adjusted through the + * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} method + * when the job is originally scheduled. The job's initial + * requirements are preserved when jobs are rescheduled, regardless of backed-off + * policy. + * <p class="note"> + * A job running while the device is dozing will not be rescheduled with the normal back-off + * policy. Instead, the job will be re-added to the queue and executed again during + * a future idle maintenance window. * </p> * - * @param params Parameters specifying system-provided info about this job, this was given to - * your application in {@link #onStartJob(JobParameters)}. - * @param needsReschedule True if this job should be rescheduled according to the back-off - * criteria specified at schedule-time. False otherwise. + * @param params The parameters identifying this job, as supplied to + * the job in the {@link #onStartJob(JobParameters)} callback. + * @param wantsReschedule {@code true} if this job should be rescheduled according + * to the back-off criteria specified when it was first scheduled; {@code false} + * otherwise. */ - public final void jobFinished(JobParameters params, boolean needsReschedule) { - mEngine.jobFinished(params, needsReschedule); + public final void jobFinished(JobParameters params, boolean wantsReschedule) { + mEngine.jobFinished(params, wantsReschedule); } -}
\ No newline at end of file +} diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index a8b8c4b5cd43..386239cf4f93 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -796,7 +796,7 @@ public class ResourcesImpl { dr = Drawable.createFromResourceStream(wrapper, value, is, file, null); is.close(); } - } catch (Exception e) { + } catch (Exception | StackOverflowError e) { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); final NotFoundException rnf = new NotFoundException( "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id)); diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl index f8f28134063d..daacc4e832f9 100644 --- a/core/java/android/os/IStatsManager.aidl +++ b/core/java/android/os/IStatsManager.aidl @@ -45,4 +45,20 @@ interface IStatsManager { * Two-way binder call so that caller's method (and corresponding wakelocks) will linger. */ void informPollAlarmFired(); + + /** + * Inform statsd what the version and package are for each uid. Note that each array should + * have the same number of elements, and version[i] and package[i] correspond to uid[i]. + */ + oneway void informAllUidData(in int[] uid, in int[] version, in String[] app); + + /** + * Inform statsd what the uid and version are for one app that was updated. + */ + oneway void informOnePackage(in String app, in int uid, in int version); + + /** + * Inform stats that an app was removed. + */ + oneway void informOnePackageRemoved(in String app, in int uid); } diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 24260c4f32c3..fba358cf4c1b 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -299,7 +299,7 @@ public class DynamicLayout extends Layout private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt(); - private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3); + private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3); } /** @@ -440,7 +440,7 @@ public class DynamicLayout extends Layout mEllipsizeAt = null; } - mObjects = new PackedObjectVector<Directions>(1); + mObjects = new PackedObjectVector<>(1); // Initial state is a single line with 0 characters (0 to 0), with top at 0 and bottom at // whatever is natural, and undefined ellipsis. @@ -1050,7 +1050,7 @@ public class DynamicLayout extends Layout private static class ChangeWatcher implements TextWatcher, SpanWatcher { public ChangeWatcher(DynamicLayout layout) { - mLayout = new WeakReference<DynamicLayout>(layout); + mLayout = new WeakReference<>(layout); } private void reflow(CharSequence s, int where, int before, int after) { diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 60fff7387c75..ac5c2e926874 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -319,8 +319,6 @@ public abstract class Layout { private float getJustifyWidth(int lineNum) { Alignment paraAlign = mAlignment; - TabStops tabStops = null; - boolean tabStopsIsInitialized = false; int left = 0; int right = mWidth; @@ -371,10 +369,6 @@ public abstract class Layout { } } - if (getLineContainsTab(lineNum)) { - tabStops = new TabStops(TAB_INCREMENT, spans); - } - final Alignment align; if (paraAlign == Alignment.ALIGN_LEFT) { align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE; @@ -1423,7 +1417,6 @@ public abstract class Layout { float dist = Math.abs(getHorizontal(max, primary) - horiz); if (dist <= bestdist) { - bestdist = dist; best = max; } @@ -1570,7 +1563,7 @@ public abstract class Layout { // XXX: we don't care about tabs tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null); caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft); - tl = TextLine.recycle(tl); + TextLine.recycle(tl); return caret; } @@ -1894,10 +1887,7 @@ public abstract class Layout { int margin = 0; - boolean isFirstParaLine = lineStart == 0 || - spanned.charAt(lineStart - 1) == '\n'; - - boolean useFirstLineMargin = isFirstParaLine; + boolean useFirstLineMargin = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n'; for (int i = 0; i < spans.length; i++) { if (spans[i] instanceof LeadingMarginSpan2) { int spStart = spanned.getSpanStart(spans[i]); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 961cd8eef530..4b6b6ae8bf83 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -433,7 +433,6 @@ public class StaticLayout extends Layout { * + addStyleRun (a text run, to be measured in native code) * + addReplacementRun (a replacement run, width is given) * - * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis). * Run nComputeLineBreaks() to obtain line breaks for the paragraph. * * After all paragraphs, call finish() to release expensive buffers. @@ -441,8 +440,6 @@ public class StaticLayout extends Layout { private Pair<String, long[]> getLocaleAndHyphenatorIfChanged(TextPaint paint) { final LocaleList locales = paint.getTextLocales(); - final String languageTags; - long[] hyphenators; if (!locales.equals(mLocales)) { mLocales = locales; return new Pair(locales.toLanguageTags(), getHyphenators(locales)); @@ -521,7 +518,7 @@ public class StaticLayout extends Layout { private LocaleList mLocales; - private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3); + private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3); } public StaticLayout(CharSequence source, TextPaint paint, @@ -866,10 +863,9 @@ public class StaticLayout extends Layout { spanEndCacheCount++; } - nGetWidths(b.mNativePtr, widths); int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks, lineBreaks.widths, lineBreaks.ascents, lineBreaks.descents, lineBreaks.flags, - lineBreaks.breaks.length); + lineBreaks.breaks.length, widths); final int[] breaks = lineBreaks.breaks; final float[] lineWidths = lineBreaks.widths; @@ -947,10 +943,10 @@ public class StaticLayout extends Layout { boolean moreChars = (endPos < bufEnd); final int ascent = fallbackLineSpacing - ? Math.min(fmAscent, (int) Math.round(ascents[breakIndex])) + ? Math.min(fmAscent, Math.round(ascents[breakIndex])) : fmAscent; final int descent = fallbackLineSpacing - ? Math.max(fmDescent, (int) Math.round(descents[breakIndex])) + ? Math.max(fmDescent, Math.round(descents[breakIndex])) : fmDescent; v = out(source, here, endPos, ascent, descent, fmTop, fmBottom, @@ -1177,7 +1173,7 @@ public class StaticLayout extends Layout { mWorkPaint.set(paint); do { final float ellipsizedWidth = guessEllipsis(text, lineStart, lineEnd, widths, - widthStart, tempAvail, where, line, textWidth, mWorkPaint, forceEllipsis, dir); + widthStart, tempAvail, where, line, mWorkPaint, forceEllipsis, dir); if (ellipsizedWidth <= avail) { lineFits = true; } else { @@ -1207,7 +1203,7 @@ public class StaticLayout extends Layout { // This method temporarily modifies the TextPaint passed to it, so the TextPaint passed to it // should not be accessed while the method is running. private float guessEllipsis(CharSequence text, int lineStart, int lineEnd, float[] widths, - int widthStart, float avail, TextUtils.TruncateAt where, int line, float textWidth, + int widthStart, float avail, TextUtils.TruncateAt where, int line, TextPaint paint, boolean forceEllipsis, int dir) { final int savedHyphenEdit = paint.getHyphenEdit(); paint.setHyphenEdit(0); @@ -1551,16 +1547,17 @@ public class StaticLayout extends Layout { @FloatRange(from = 0.0f) float width, @Nullable String languageTags, @Nullable long[] hyphenators); - private static native void nGetWidths(long nativePtr, float[] widths); - // populates LineBreaks and returns the number of breaks found // // the arrays inside the LineBreaks objects are passed in as well // to reduce the number of JNI calls in the common case where the // arrays do not have to be resized + // The individual character widths will be returned in charWidths. The length of charWidths must + // be at least the length of the text. private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths, float[] recycleAscents, - float[] recycleDescents, int[] recycleFlags, int recycleLength); + float[] recycleDescents, int[] recycleFlags, int recycleLength, + float[] charWidths); private int mLineCount; private int mTopPadding, mBottomPadding; diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 2dbff100375a..20c0ed87285a 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -73,7 +73,7 @@ class TextLine { new SpanSet<ReplacementSpan>(ReplacementSpan.class); private final DecorationInfo mDecorationInfo = new DecorationInfo(); - private final ArrayList<DecorationInfo> mDecorations = new ArrayList(); + private final ArrayList<DecorationInfo> mDecorations = new ArrayList<>(); private static final TextLine[] sCached = new TextLine[3]; @@ -340,14 +340,14 @@ class TextLine { boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl; if (inSegment && advance) { - return h += measureRun(segstart, offset, j, runIsRtl, fmi); + return h + measureRun(segstart, offset, j, runIsRtl, fmi); } float w = measureRun(segstart, j, j, runIsRtl, fmi); h += advance ? w : -w; if (inSegment) { - return h += measureRun(segstart, offset, j, runIsRtl, null); + return h + measureRun(segstart, offset, j, runIsRtl, null); } if (codept == '\t') { @@ -828,14 +828,14 @@ class TextLine { } if (info.isUnderlineText) { final float thickness = - Math.max(((Paint) wp).getUnderlineThickness(), 1.0f); + Math.max(wp.getUnderlineThickness(), 1.0f); drawStroke(wp, c, wp.getColor(), wp.getUnderlinePosition(), thickness, decorationXLeft, decorationXRight, y); } if (info.isStrikeThruText) { final float thickness = - Math.max(((Paint) wp).getStrikeThruThickness(), 1.0f); + Math.max(wp.getStrikeThruThickness(), 1.0f); drawStroke(wp, c, wp.getColor(), wp.getStrikeThruPosition(), thickness, decorationXLeft, decorationXRight, y); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 36fd991cc78d..439e5e50c8c5 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -3588,7 +3588,7 @@ public class BatteryStatsImpl extends BatteryStats { public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime, long realtime) { - final boolean screenOff = isScreenOff(screenState) || isScreenDoze(screenState); + final boolean screenOff = !isScreenOn(screenState); final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning(); final boolean updateOnBatteryScreenOffTimeBase = (unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning(); @@ -9463,7 +9463,7 @@ public class BatteryStatsImpl extends BatteryStats { } public boolean isScreenOn(int state) { - return state == Display.STATE_ON; + return state == Display.STATE_ON || state == Display.STATE_VR; } public boolean isScreenOff(int state) { diff --git a/core/java/com/android/internal/os/LoggingPrintStream.java b/core/java/com/android/internal/os/LoggingPrintStream.java index f14394ad09ce..d27874cd3be2 100644 --- a/core/java/com/android/internal/os/LoggingPrintStream.java +++ b/core/java/com/android/internal/os/LoggingPrintStream.java @@ -28,12 +28,15 @@ import java.nio.charset.CodingErrorAction; import java.util.Formatter; import java.util.Locale; +import com.android.internal.annotations.VisibleForTesting; + /** * A print stream which logs output line by line. * * {@hide} */ -abstract class LoggingPrintStream extends PrintStream { +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public abstract class LoggingPrintStream extends PrintStream { private final StringBuilder builder = new StringBuilder(); diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index 83ffeffd2da3..1f7277a7e98d 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -166,7 +166,7 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, jobject recycle, jintArray recycleBreaks, jfloatArray recycleWidths, jfloatArray recycleAscents, jfloatArray recycleDescents, jintArray recycleFlags, - jint recycleLength) { + jint recycleLength, jfloatArray charWidths) { minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); size_t nBreaks = b->computeBreaks(); @@ -175,6 +175,8 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, recycleFlags, recycleLength, nBreaks, b->getBreaks(), b->getWidths(), b->getAscents(), b->getDescents(), b->getFlags()); + env->SetFloatArrayRegion(charWidths, 0, b->size(), b->charWidths()); + b->finish(); return static_cast<jint>(nBreaks); @@ -256,11 +258,6 @@ static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr, b->addReplacement(start, end, width, langTagsString.get(), makeHyphenators(env, hyphenators)); } -static void nGetWidths(JNIEnv* env, jclass, jlong nativePtr, jfloatArray widths) { - minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr); - env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths()); -} - static const JNINativeMethod gMethods[] = { // TODO performance: many of these are candidates for fast jni, awaiting guidance {"nNewBuilder", "()J", (void*) nNewBuilder}, @@ -269,8 +266,7 @@ static const JNINativeMethod gMethods[] = { {"nSetupParagraph", "(J[CIFIF[IIIIZ[I[I[II)V", (void*) nSetupParagraph}, {"nAddStyleRun", "(JJIIZLjava/lang/String;[J)V", (void*) nAddStyleRun}, {"nAddReplacementRun", "(JIIFLjava/lang/String;[J)V", (void*) nAddReplacementRun}, - {"nGetWidths", "(J[F)V", (void*) nGetWidths}, - {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II)I", + {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II[F)I", (void*) nComputeLineBreaks} }; diff --git a/core/jni/eventlog_helper.h b/core/jni/eventlog_helper.h index 1101b837b589..3a05195ebc9e 100644 --- a/core/jni/eventlog_helper.h +++ b/core/jni/eventlog_helper.h @@ -17,6 +17,8 @@ #ifndef FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_ #define FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_ +#include <memory> + #include <fcntl.h> #include <android-base/macros.h> @@ -26,6 +28,8 @@ #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/ScopedUtfChars.h> #include "core_jni_helpers.h" #include "jni.h" @@ -91,20 +95,14 @@ public: android_log_event_list ctx(tag); // Don't throw NPE -- I feel like it's sort of mean for a logging function // to be all crashy if you pass in NULL -- but make the NULL value explicit. - if (value != NULL) { - const char *str = env->GetStringUTFChars(value, NULL); - ctx << str; - env->ReleaseStringUTFChars(value, str); - } else { - ctx << "NULL"; - } + ctx << (value != nullptr ? ScopedUtfChars(env, value).c_str() : "NULL"); return ctx.write(LogID); } static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag, jobjectArray value) { android_log_event_list ctx(tag); - if (value == NULL) { + if (value == nullptr) { ctx << "[NULL]"; return ctx.write(LogID); } @@ -112,26 +110,23 @@ public: jsize copied = 0, num = env->GetArrayLength(value); for (; copied < num && copied < 255; ++copied) { if (ctx.status()) break; - jobject item = env->GetObjectArrayElement(value, copied); - if (item == NULL) { + ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(value, copied)); + if (item == nullptr) { ctx << "NULL"; - } else if (env->IsInstanceOf(item, gStringClass)) { - const char *str = env->GetStringUTFChars((jstring) item, NULL); - ctx << str; - env->ReleaseStringUTFChars((jstring) item, str); - } else if (env->IsInstanceOf(item, gIntegerClass)) { - ctx << (int32_t)env->GetIntField(item, gIntegerValueID); - } else if (env->IsInstanceOf(item, gLongClass)) { - ctx << (int64_t)env->GetLongField(item, gLongValueID); - } else if (env->IsInstanceOf(item, gFloatClass)) { - ctx << (float)env->GetFloatField(item, gFloatValueID); + } else if (env->IsInstanceOf(item.get(), gStringClass)) { + ctx << ScopedUtfChars(env, (jstring) item.get()).c_str(); + } else if (env->IsInstanceOf(item.get(), gIntegerClass)) { + ctx << (int32_t)env->GetIntField(item.get(), gIntegerValueID); + } else if (env->IsInstanceOf(item.get(), gLongClass)) { + ctx << (int64_t)env->GetLongField(item.get(), gLongValueID); + } else if (env->IsInstanceOf(item.get(), gFloatClass)) { + ctx << (float)env->GetFloatField(item.get(), gFloatValueID); } else { jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid payload item type"); return -1; } - env->DeleteLocalRef(item); } return ctx.write(LogID); } @@ -140,39 +135,37 @@ public: readEvents(env, loggerMode, nullptr, startTime, out); } - static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime, + static void readEvents(JNIEnv* env, int loggerMode, jintArray jTags, jlong startTime, jobject out) { - struct logger_list *logger_list; + std::unique_ptr<struct logger_list, decltype(&android_logger_list_close)> logger_list( + nullptr, android_logger_list_close); if (startTime) { - logger_list = android_logger_list_alloc_time(loggerMode, - log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0); + logger_list.reset(android_logger_list_alloc_time(loggerMode, + log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0)); } else { - logger_list = android_logger_list_alloc(loggerMode, 0, 0); + logger_list.reset(android_logger_list_alloc(loggerMode, 0, 0)); } if (!logger_list) { jniThrowIOException(env, errno); return; } - if (!android_logger_open(logger_list, LogID)) { + if (!android_logger_open(logger_list.get(), LogID)) { jniThrowIOException(env, errno); - android_logger_list_free(logger_list); return; } - jsize tagLength = 0; - jint *tagValues = nullptr; - if (tags != nullptr) { - tagLength = env->GetArrayLength(tags); - tagValues = env->GetIntArrayElements(tags, NULL); + ScopedIntArrayRO tags(env); + if (jTags != nullptr) { + tags.reset(jTags); } while (1) { log_msg log_msg; - int ret = android_logger_list_read(logger_list, &log_msg); + int ret = android_logger_list_read(logger_list.get(), &log_msg); if (ret == 0) { - break; + return; } if (ret < 0) { if (ret == -EINTR) { @@ -183,7 +176,7 @@ public: } else if (ret != -EAGAIN) { jniThrowIOException(env, -ret); // Will throw on return } - break; + return; } if (log_msg.id() != LogID) { @@ -192,10 +185,10 @@ public: int32_t tag = * (int32_t *) log_msg.msg(); - if (tags != nullptr) { + if (jTags != nullptr) { bool found = false; - for (int i = 0; !found && i < tagLength; ++i) { - found = (tag == tagValues[i]); + for (size_t i = 0; !found && i < tags.size(); ++i) { + found = (tag == tags[i]); } if (!found) { continue; @@ -203,33 +196,27 @@ public: } jsize len = ret; - jbyteArray array = env->NewByteArray(len); - if (array == NULL) { - break; + ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(len)); + if (array == nullptr) { + return; } - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, log_msg.buf, len); - env->ReleaseByteArrayElements(array, bytes, 0); + { + ScopedByteArrayRW bytes(env, array.get()); + memcpy(bytes.get(), log_msg.buf, len); + } - jobject event = env->NewObject(gEventClass, gEventInitID, array); - if (event == NULL) { - break; + ScopedLocalRef<jobject> event(env, + env->NewObject(gEventClass, gEventInitID, array.get())); + if (event == nullptr) { + return; } - env->CallBooleanMethod(out, gCollectionAddID, event); - env->DeleteLocalRef(event); - env->DeleteLocalRef(array); + env->CallBooleanMethod(out, gCollectionAddID, event.get()); if (env->ExceptionCheck() == JNI_TRUE) { - break; + return; } } - - android_logger_list_close(logger_list); - - if (tags != nullptr) { - env->ReleaseIntArrayElements(tags, tagValues, 0); - } } private: diff --git a/core/res/res/values-mcc001-mnc01/strings.xml b/core/res/res/values-mcc001-mnc01/strings.xml new file mode 100644 index 000000000000..96af975b50b5 --- /dev/null +++ b/core/res/res/values-mcc001-mnc01/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, 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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> +</resources> diff --git a/core/res/res/values-mcc310-mnc030/strings.xml b/core/res/res/values-mcc310-mnc030/strings.xml index a3fea29070f5..6a404d5cee5f 100644 --- a/core/res/res/values-mcc310-mnc030/strings.xml +++ b/core/res/res/values-mcc310-mnc030/strings.xml @@ -20,4 +20,5 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string> <string name="mmcc_illegal_ms">SIM not allowed MM#3</string> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> </resources> diff --git a/core/res/res/values-mcc310-mnc150/strings.xml b/core/res/res/values-mcc310-mnc150/strings.xml new file mode 100644 index 000000000000..96af975b50b5 --- /dev/null +++ b/core/res/res/values-mcc310-mnc150/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, 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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> +</resources> diff --git a/core/res/res/values-mcc310-mnc170/strings.xml b/core/res/res/values-mcc310-mnc170/strings.xml index a3fea29070f5..6a404d5cee5f 100644 --- a/core/res/res/values-mcc310-mnc170/strings.xml +++ b/core/res/res/values-mcc310-mnc170/strings.xml @@ -20,4 +20,5 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string> <string name="mmcc_illegal_ms">SIM not allowed MM#3</string> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> </resources> diff --git a/core/res/res/values-mcc310-mnc280/strings.xml b/core/res/res/values-mcc310-mnc280/strings.xml index a3fea29070f5..6a404d5cee5f 100644 --- a/core/res/res/values-mcc310-mnc280/strings.xml +++ b/core/res/res/values-mcc310-mnc280/strings.xml @@ -20,4 +20,5 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string> <string name="mmcc_illegal_ms">SIM not allowed MM#3</string> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> </resources> diff --git a/core/res/res/values-mcc310-mnc410/strings.xml b/core/res/res/values-mcc310-mnc410/strings.xml index a3fea29070f5..6a404d5cee5f 100644 --- a/core/res/res/values-mcc310-mnc410/strings.xml +++ b/core/res/res/values-mcc310-mnc410/strings.xml @@ -20,4 +20,5 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string> <string name="mmcc_illegal_ms">SIM not allowed MM#3</string> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> </resources> diff --git a/core/res/res/values-mcc310-mnc560/strings.xml b/core/res/res/values-mcc310-mnc560/strings.xml index a3fea29070f5..6a404d5cee5f 100644 --- a/core/res/res/values-mcc310-mnc560/strings.xml +++ b/core/res/res/values-mcc310-mnc560/strings.xml @@ -20,4 +20,5 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string> <string name="mmcc_illegal_ms">SIM not allowed MM#3</string> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> </resources> diff --git a/core/res/res/values-mcc310-mnc950/strings.xml b/core/res/res/values-mcc310-mnc950/strings.xml index a3fea29070f5..6a404d5cee5f 100644 --- a/core/res/res/values-mcc310-mnc950/strings.xml +++ b/core/res/res/values-mcc310-mnc950/strings.xml @@ -20,4 +20,5 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string> <string name="mmcc_illegal_ms">SIM not allowed MM#3</string> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> </resources> diff --git a/core/res/res/values-mcc311-mnc180/strings.xml b/core/res/res/values-mcc311-mnc180/strings.xml index a3fea29070f5..6a404d5cee5f 100644 --- a/core/res/res/values-mcc311-mnc180/strings.xml +++ b/core/res/res/values-mcc311-mnc180/strings.xml @@ -20,4 +20,5 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string> <string name="mmcc_illegal_ms">SIM not allowed MM#3</string> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> </resources> diff --git a/core/res/res/values-mcc312-mnc670/strings.xml b/core/res/res/values-mcc312-mnc670/strings.xml new file mode 100644 index 000000000000..96af975b50b5 --- /dev/null +++ b/core/res/res/values-mcc312-mnc670/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, 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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> +</resources> diff --git a/core/res/res/values-mcc313-mnc100/strings.xml b/core/res/res/values-mcc313-mnc100/strings.xml new file mode 100644 index 000000000000..96af975b50b5 --- /dev/null +++ b/core/res/res/values-mcc313-mnc100/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* //device/apps/common/assets/res/any/strings.xml +** +** Copyright 2006, 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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="mmcc_illegal_me">Phone not allowed MM#6</string> +</resources> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index dbc9e5d55e37..15eab1f72aab 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -36,7 +36,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ ub-uiautomator \ platform-test-annotations \ compatibility-device-util \ - truth-prebuilt + truth-prebuilt \ + print-test-util-lib LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index ac5d224c9030..9c0543b18f8b 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -102,6 +102,7 @@ <!-- os storage test permissions --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.ASEC_ACCESS" /> + <uses-permission android:name="android.permission.ASEC_ACCESS" /> <uses-permission android:name="android.permission.ASEC_CREATE" /> <uses-permission android:name="android.permission.ASEC_DESTROY" /> <uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT" /> @@ -1345,10 +1346,12 @@ </intent-filter> </activity> - <activity android:name="android.print.PrintTestActivity"/> + <activity + android:name="android.print.test.PrintDocumentActivity" + android:theme="@style/Theme" /> <service - android:name="android.print.mockservice.MockPrintService" + android:name="android.print.test.services.FirstPrintService" android:permission="android.permission.BIND_PRINT_SERVICE"> <intent-filter> <action android:name="android.printservice.PrintService" /> @@ -1360,9 +1363,10 @@ </service> <activity - android:name="android.print.mockservice.SettingsActivity" + android:name="android.print.test.services.SettingsActivity" android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY" - android:exported="true"> + android:exported="true" + android:theme="@style/Theme"> </activity> <activity diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java deleted file mode 100644 index a70c6046b230..000000000000 --- a/core/tests/coretests/src/android/print/BasePrintTest.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (C) 2016 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.print; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doCallRealMethod; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.app.Instrumentation; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.CancellationSignal; -import android.os.ParcelFileDescriptor; -import android.os.SystemClock; -import android.print.mockservice.PrintServiceCallbacks; -import android.print.mockservice.PrinterDiscoverySessionCallbacks; -import android.print.mockservice.StubbablePrinterDiscoverySession; -import android.printservice.CustomPrinterIconCallback; -import android.printservice.PrintJob; -import android.printservice.PrintService; -import android.support.test.InstrumentationRegistry; -import android.support.test.rule.ActivityTestRule; -import android.support.test.uiautomator.UiDevice; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.mockito.stubbing.Answer; - -import java.io.FileInputStream; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeoutException; - -/** - * This is the base class for print tests. - */ -abstract class BasePrintTest { - protected static final long OPERATION_TIMEOUT = 30000; - private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success"; - private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT - - private android.print.PrintJob mPrintJob; - - private CallCounter mStartCallCounter; - private CallCounter mStartSessionCallCounter; - - private static Instrumentation sInstrumentation; - private static UiDevice sUiDevice; - - @Rule - public ActivityTestRule<PrintTestActivity> mActivityRule = - new ActivityTestRule<>(PrintTestActivity.class, false, true); - - /** - * {@link Runnable} that can throw and {@link Exception} - */ - interface Invokable { - /** - * Execute the invokable - * - * @throws Exception - */ - void run() throws Exception; - } - - /** - * Assert that the invokable throws an expectedException - * - * @param invokable The {@link Invokable} to run - * @param expectedClass The {@link Exception} that is supposed to be thrown - */ - void assertException(Invokable invokable, Class<? extends Exception> expectedClass) - throws Exception { - try { - invokable.run(); - } catch (Exception e) { - if (e.getClass().isAssignableFrom(expectedClass)) { - return; - } else { - throw e; - } - } - - throw new AssertionError("No exception thrown"); - } - - /** - * Return the UI device - * - * @return the UI device - */ - public UiDevice getUiDevice() { - return sUiDevice; - } - - protected static Instrumentation getInstrumentation() { - return sInstrumentation; - } - - @BeforeClass - public static void setUpClass() throws Exception { - sInstrumentation = InstrumentationRegistry.getInstrumentation(); - assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature( - PackageManager.FEATURE_PRINTING)); - - sUiDevice = UiDevice.getInstance(sInstrumentation); - - // Make sure we start with a clean slate. - clearPrintSpoolerData(); - - // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 - // Dexmaker is used by mockito. - System.setProperty("dexmaker.dexcache", getInstrumentation() - .getTargetContext().getCacheDir().getPath()); - } - - @Before - public void initCounters() throws Exception { - // Initialize the latches. - mStartCallCounter = new CallCounter(); - mStartSessionCallCounter = new CallCounter(); - } - - @Before - public void unlockScreen() throws Exception { - // Unlock screen. - runShellCommand(getInstrumentation(), "input keyevent KEYCODE_WAKEUP"); - runShellCommand(getInstrumentation(), "wm dismiss-keyguard"); - } - - @After - public void exitActivities() throws Exception { - // Exit print spooler - getUiDevice().pressBack(); - getUiDevice().pressBack(); - } - - protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter, - final PrintAttributes attributes) { - // Initiate printing as if coming from the app. - getInstrumentation().runOnMainSync(() -> { - PrintManager printManager = (PrintManager) getActivity() - .getSystemService(Context.PRINT_SERVICE); - mPrintJob = printManager.print("Print job", adapter, attributes); - }); - - return mPrintJob; - } - - protected void onStartCalled() { - mStartCallCounter.call(); - } - - protected void onPrinterDiscoverySessionStartCalled() { - mStartSessionCallCounter.call(); - } - - protected void waitForPrinterDiscoverySessionStartCallbackCalled() { - waitForCallbackCallCount(mStartSessionCallCounter, 1, - "Did not get expected call to onStartPrinterDiscoverySession."); - } - - protected void waitForStartAdapterCallbackCalled() { - waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start."); - } - - private static void waitForCallbackCallCount(CallCounter counter, int count, String message) { - try { - counter.waitForCount(count, OPERATION_TIMEOUT); - } catch (TimeoutException te) { - fail(message); - } - } - - protected PrintTestActivity getActivity() { - return mActivityRule.getActivity(); - } - - public static String runShellCommand(Instrumentation instrumentation, String cmd) - throws IOException { - ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd); - byte[] buf = new byte[512]; - int bytesRead; - FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); - StringBuilder stdout = new StringBuilder(); - while ((bytesRead = fis.read(buf)) != -1) { - stdout.append(new String(buf, 0, bytesRead)); - } - fis.close(); - return stdout.toString(); - } - - protected static void clearPrintSpoolerData() throws Exception { - assertTrue("failed to clear print spooler data", - runShellCommand(getInstrumentation(), String.format( - "pm clear --user %d %s", CURRENT_USER_ID, - PrintManager.PRINT_SPOOLER_PACKAGE_NAME)) - .contains(PM_CLEAR_SUCCESS_OUTPUT)); - } - - @SuppressWarnings("unchecked") - protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks( - Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, - Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, - Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking, - Answer<Void> onDestroy) { - PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class); - - doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class)); - when(callbacks.getSession()).thenCallRealMethod(); - - if (onStartPrinterDiscovery != null) { - doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery( - any(List.class)); - } - if (onStopPrinterDiscovery != null) { - doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery(); - } - if (onValidatePrinters != null) { - doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters( - any(List.class)); - } - if (onStartPrinterStateTracking != null) { - doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking( - any(PrinterId.class)); - } - if (onRequestCustomPrinterIcon != null) { - doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon( - any(PrinterId.class), any(CancellationSignal.class), - any(CustomPrinterIconCallback.class)); - } - if (onStopPrinterStateTracking != null) { - doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking( - any(PrinterId.class)); - } - if (onDestroy != null) { - doAnswer(onDestroy).when(callbacks).onDestroy(); - } - - return callbacks; - } - - protected PrintServiceCallbacks createMockPrintServiceCallbacks( - Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, - Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) { - final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class); - - doCallRealMethod().when(service).setService(any(PrintService.class)); - when(service.getService()).thenCallRealMethod(); - - if (onCreatePrinterDiscoverySessionCallbacks != null) { - doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service) - .onCreatePrinterDiscoverySessionCallbacks(); - } - if (onPrintJobQueued != null) { - doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class)); - } - if (onRequestCancelPrintJob != null) { - doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob( - any(PrintJob.class)); - } - - return service; - } - - private static final class CallCounter { - private final Object mLock = new Object(); - - private int mCallCount; - - public void call() { - synchronized (mLock) { - mCallCount++; - mLock.notifyAll(); - } - } - - int getCallCount() { - synchronized (mLock) { - return mCallCount; - } - } - - public void waitForCount(int count, long timeoutMillis) throws TimeoutException { - synchronized (mLock) { - final long startTimeMillis = SystemClock.uptimeMillis(); - while (mCallCount < count) { - try { - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; - if (remainingTimeMillis <= 0) { - throw new TimeoutException(); - } - mLock.wait(timeoutMillis); - } catch (InterruptedException ie) { - /* ignore */ - } - } - } - } - } -} diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java index 45e3f679fe55..5d12f7e43558 100644 --- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java +++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java @@ -16,6 +16,8 @@ package android.print; +import static android.print.test.Utils.assertException; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -33,10 +35,11 @@ import android.os.UserHandle; import android.print.PrintAttributes.Margins; import android.print.PrintAttributes.MediaSize; import android.print.PrintAttributes.Resolution; -import android.print.mockservice.MockPrintService; -import android.print.mockservice.PrintServiceCallbacks; -import android.print.mockservice.PrinterDiscoverySessionCallbacks; -import android.print.mockservice.StubbablePrinterDiscoverySession; +import android.print.test.BasePrintTest; +import android.print.test.services.FirstPrintService; +import android.print.test.services.PrintServiceCallbacks; +import android.print.test.services.PrinterDiscoverySessionCallbacks; +import android.print.test.services.StubbablePrinterDiscoverySession; import android.printservice.recommendation.IRecommendationsChangeListener; import android.support.test.filters.LargeTest; import android.support.test.filters.MediumTest; @@ -149,7 +152,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { session.addPrinters(printers); } - onPrinterDiscoverySessionStartCalled(); + onPrinterDiscoverySessionCreateCalled(); return null; }, null, null, null, null, null, null), null, null); @@ -200,13 +203,18 @@ public class IPrintManagerParametersTest extends BasePrintTest { } private void startPrinting() { - mGoodPrintJob = print(createMockAdapter(), null); + mGoodPrintJob = print(createMockAdapter(), (PrintAttributes) null); // Wait for PrintActivity to be ready - waitForStartAdapterCallbackCalled(); + waitForAdapterStartCallbackCalled(); // Wait for printer discovery session to be ready - waitForPrinterDiscoverySessionStartCallbackCalled(); + waitForPrinterDiscoverySessionCreateCallbackCalled(); + } + + private void endPrinting() { + getUiDevice().pressBack(); + getUiDevice().pressBack(); } /** @@ -220,7 +228,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { @Before public void setUpMockService() throws Exception { - MockPrintService.setCallbacks(createMockCallbacks()); + FirstPrintService.setCallbacks(createMockCallbacks()); mIPrintManager = IPrintManager.Stub .asInterface(ServiceManager.getService(Context.PRINT_SERVICE)); @@ -231,7 +239,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testGetPrintJobInfo() throws Exception { + public void testGetPrintJobInfo() throws Throwable { startPrinting(); assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(), @@ -244,6 +252,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { SecurityException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -251,7 +261,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testGetPrintJobInfos() throws Exception { + public void testGetPrintJobInfos() throws Throwable { startPrinting(); List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId); @@ -269,6 +279,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { SecurityException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -276,7 +288,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testPrint() throws Exception { + public void testPrint() throws Throwable { final String name = "dummy print job"; final IPrintDocumentAdapter adapter = new PrintManager @@ -303,6 +315,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { getActivity().getPackageName(), BAD_APP_ID, mUserId), SecurityException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -310,7 +324,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testCancelPrintJob() throws Exception { + public void testCancelPrintJob() throws Throwable { startPrinting(); // Invalid print jobs IDs do not produce an exception @@ -325,6 +339,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { // Must be last as otherwise mGoodPrintJob will not be good anymore mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), mAppId, mUserId); + + endPrinting(); } /** @@ -332,7 +348,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testRestartPrintJob() throws Exception { + public void testRestartPrintJob() throws Throwable { startPrinting(); mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId); @@ -346,6 +362,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { SecurityException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -353,7 +371,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testAddPrintJobStateChangeListener() throws Exception { + @NoActivity + public void testAddPrintJobStateChangeListener() throws Throwable { final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener(); mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId); @@ -373,7 +392,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testRemovePrintJobStateChangeListener() throws Exception { + @NoActivity + public void testRemovePrintJobStateChangeListener() throws Throwable { final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener(); mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId); @@ -394,7 +414,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testAddPrintServicesChangeListener() throws Exception { + @NoActivity + public void testAddPrintServicesChangeListener() throws Throwable { final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener(); assertException(() -> mIPrintManager.addPrintServicesChangeListener(listener, mUserId), @@ -411,7 +432,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testRemovePrintServicesChangeListener() throws Exception { + @NoActivity + public void testRemovePrintServicesChangeListener() throws Throwable { final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener(); assertException(() -> mIPrintManager.removePrintServicesChangeListener(listener, mUserId), @@ -426,7 +448,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testGetPrintServices() throws Exception { + @NoActivity + public void testGetPrintServices() throws Throwable { assertException(() -> mIPrintManager.getPrintServices(PrintManager.ALL_SERVICES, mUserId), SecurityException.class); @@ -444,7 +467,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testSetPrintServiceEnabled() throws Exception { + @NoActivity + public void testSetPrintServiceEnabled() throws Throwable { assertException( () -> mIPrintManager.setPrintServiceEnabled(new ComponentName("bad", "name"), true, mUserId), SecurityException.class); @@ -460,7 +484,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testAddPrintServiceRecommendationsChangeListener() throws Exception { + @NoActivity + public void testAddPrintServiceRecommendationsChangeListener() throws Throwable { final IRecommendationsChangeListener listener = createMockIPrintServiceRecommendationsChangeListener(); @@ -479,7 +504,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testRemovePrintServiceRecommendationsChangeListener() throws Exception { + @NoActivity + public void testRemovePrintServiceRecommendationsChangeListener() throws Throwable { final IRecommendationsChangeListener listener = createMockIPrintServiceRecommendationsChangeListener(); @@ -498,7 +524,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testGetPrintServiceRecommendations() throws Exception { + @NoActivity + public void testGetPrintServiceRecommendations() throws Throwable { assertException(() -> mIPrintManager.getPrintServiceRecommendations(mUserId), SecurityException.class); @@ -510,7 +537,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testCreatePrinterDiscoverySession() throws Exception { + @NoActivity + public void testCreatePrinterDiscoverySession() throws Throwable { final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); mIPrintManager.createPrinterDiscoverySession(listener, mUserId); @@ -533,7 +561,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testStartPrinterDiscovery() throws Exception { + public void testStartPrinterDiscovery() throws Throwable { startPrinting(); final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); @@ -562,6 +590,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { NullPointerException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -569,7 +599,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testStopPrinterDiscovery() throws Exception { + @NoActivity + public void testStopPrinterDiscovery() throws Throwable { final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); mIPrintManager.startPrinterDiscovery(listener, null, mUserId); @@ -590,7 +621,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testValidatePrinters() throws Exception { + public void testValidatePrinters() throws Throwable { startPrinting(); final List<PrinterId> goodPrinters = new ArrayList<>(); @@ -617,6 +648,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { NullPointerException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -624,7 +657,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testStartPrinterStateTracking() throws Exception { + public void testStartPrinterStateTracking() throws Throwable { startPrinting(); mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId); @@ -636,6 +669,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { NullPointerException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -643,7 +678,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testGetCustomPrinterIcon() throws Exception { + public void testGetCustomPrinterIcon() throws Throwable { startPrinting(); mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId); @@ -655,6 +690,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { NullPointerException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -662,7 +699,7 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @LargeTest @Test - public void testStopPrinterStateTracking() throws Exception { + public void testStopPrinterStateTracking() throws Throwable { startPrinting(); mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId); @@ -679,6 +716,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { NullPointerException.class); // Cannot test bad user Id as these tests are allowed to call across users + + endPrinting(); } /** @@ -686,7 +725,8 @@ public class IPrintManagerParametersTest extends BasePrintTest { */ @MediumTest @Test - public void testDestroyPrinterDiscoverySession() throws Exception { + @NoActivity + public void testDestroyPrinterDiscoverySession() throws Throwable { final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver(); mIPrintManager.createPrinterDiscoverySession(listener, mUserId); diff --git a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java deleted file mode 100644 index 9c11c22282d7..000000000000 --- a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2016 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.print.mockservice; - -public class MockPrintService extends StubbablePrintService { - - private static final Object sLock = new Object(); - - private static PrintServiceCallbacks sCallbacks; - - public static void setCallbacks(PrintServiceCallbacks callbacks) { - synchronized (sLock) { - sCallbacks = callbacks; - } - } - - @Override - protected PrintServiceCallbacks getCallbacks() { - synchronized (sLock) { - if (sCallbacks != null) { - sCallbacks.setService(this); - } - return sCallbacks; - } - } -} diff --git a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java deleted file mode 100644 index 4e892072f0cb..000000000000 --- a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2016 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.print.mockservice; - -import android.printservice.PrintJob; -import android.printservice.PrintService; - -public abstract class PrintServiceCallbacks { - - private PrintService mService; - - public PrintService getService() { - return mService; - } - - public void setService(PrintService service) { - mService = service; - } - - public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks(); - - public abstract void onRequestCancelPrintJob(PrintJob printJob); - - public abstract void onPrintJobQueued(PrintJob printJob); -} diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java deleted file mode 100644 index be002e29ff68..000000000000 --- a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2016 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.print.mockservice; - -import android.os.CancellationSignal; -import android.print.PrinterId; -import android.printservice.CustomPrinterIconCallback; - -import java.util.List; - -public abstract class PrinterDiscoverySessionCallbacks { - - private StubbablePrinterDiscoverySession mSession; - - public void setSession(StubbablePrinterDiscoverySession session) { - mSession = session; - } - - public StubbablePrinterDiscoverySession getSession() { - return mSession; - } - - public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList); - - public abstract void onStopPrinterDiscovery(); - - public abstract void onValidatePrinters(List<PrinterId> printerIds); - - public abstract void onStartPrinterStateTracking(PrinterId printerId); - - public abstract void onRequestCustomPrinterIcon(PrinterId printerId, - CancellationSignal cancellationSignal, CustomPrinterIconCallback callback); - - public abstract void onStopPrinterStateTracking(PrinterId printerId); - - public abstract void onDestroy(); -} diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java deleted file mode 100644 index b58b27350c28..000000000000 --- a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2016 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.print.mockservice; - -import android.printservice.PrintJob; -import android.printservice.PrintService; -import android.printservice.PrinterDiscoverySession; - -public abstract class StubbablePrintService extends PrintService { - - @Override - public PrinterDiscoverySession onCreatePrinterDiscoverySession() { - PrintServiceCallbacks callbacks = getCallbacks(); - if (callbacks != null) { - return new StubbablePrinterDiscoverySession(this, - getCallbacks().onCreatePrinterDiscoverySessionCallbacks()); - } - return null; - } - - @Override - public void onRequestCancelPrintJob(PrintJob printJob) { - PrintServiceCallbacks callbacks = getCallbacks(); - if (callbacks != null) { - callbacks.onRequestCancelPrintJob(printJob); - } - } - - @Override - public void onPrintJobQueued(PrintJob printJob) { - PrintServiceCallbacks callbacks = getCallbacks(); - if (callbacks != null) { - callbacks.onPrintJobQueued(printJob); - } - } - - protected abstract PrintServiceCallbacks getCallbacks(); -} diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java deleted file mode 100644 index f3a5373722cc..000000000000 --- a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2016 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.print.mockservice; - -import android.support.annotation.NonNull; -import android.os.CancellationSignal; -import android.print.PrinterId; -import android.printservice.CustomPrinterIconCallback; -import android.printservice.PrintService; -import android.printservice.PrinterDiscoverySession; - -import java.util.List; - -public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession { - private final PrintService mService; - private final PrinterDiscoverySessionCallbacks mCallbacks; - - public StubbablePrinterDiscoverySession(PrintService service, - PrinterDiscoverySessionCallbacks callbacks) { - mService = service; - mCallbacks = callbacks; - if (mCallbacks != null) { - mCallbacks.setSession(this); - } - } - - public PrintService getService() { - return mService; - } - - @Override - public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) { - if (mCallbacks != null) { - mCallbacks.onStartPrinterDiscovery(priorityList); - } - } - - @Override - public void onStopPrinterDiscovery() { - if (mCallbacks != null) { - mCallbacks.onStopPrinterDiscovery(); - } - } - - @Override - public void onValidatePrinters(@NonNull List<PrinterId> printerIds) { - if (mCallbacks != null) { - mCallbacks.onValidatePrinters(printerIds); - } - } - - @Override - public void onStartPrinterStateTracking(@NonNull PrinterId printerId) { - if (mCallbacks != null) { - mCallbacks.onStartPrinterStateTracking(printerId); - } - } - - @Override - public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId, - @NonNull CancellationSignal cancellationSignal, - @NonNull CustomPrinterIconCallback callback) { - if (mCallbacks != null) { - mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback); - } - } - - @Override - public void onStopPrinterStateTracking(@NonNull PrinterId printerId) { - if (mCallbacks != null) { - mCallbacks.onStopPrinterStateTracking(printerId); - } - } - - @Override - public void onDestroy() { - if (mCallbacks != null) { - mCallbacks.onDestroy(); - } - } -} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index 461d537e86f9..4e8322183a8b 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -214,7 +214,7 @@ public class BatteryStatsNoteTest extends TestCase{ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning()); } - /** Test BatteryStatsImpl.noteScreenStateLocked. */ + /** Test BatteryStatsImpl.noteScreenStateLocked sets timebases and screen states correctly. */ @SmallTest public void testNoteScreenStateLocked() throws Exception { final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms @@ -233,4 +233,52 @@ public class BatteryStatsNoteTest extends TestCase{ assertEquals(bi.getScreenState(), Display.STATE_OFF); } + /** Test BatteryStatsImpl.noteScreenStateLocked updates timers correctly. + * + * Unknown and doze should both be subset of off state + * + * Timeline 0----100----200----310----400------------1000 + * Unknown ------- + * On ------- + * Off ------- ---------------------- + * Doze ---------------- + */ + @SmallTest + public void testNoteScreenStateTimersLocked() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + + clocks.realtime = clocks.uptime = 100; + // Device startup, setOnBatteryLocked calls updateTimebases + bi.updateTimeBasesLocked(true, Display.STATE_UNKNOWN, 100_000, 100_000); + // Turn on display at 200us + clocks.realtime = clocks.uptime = 200; + bi.noteScreenStateLocked(Display.STATE_ON); + assertEquals(150_000, bi.computeBatteryRealtime(250_000, STATS_SINCE_CHARGED)); + assertEquals(100_000, bi.computeBatteryScreenOffRealtime(250_000, STATS_SINCE_CHARGED)); + assertEquals(50_000, bi.getScreenOnTime(250_000, STATS_SINCE_CHARGED)); + assertEquals(0, bi.getScreenDozeTime(250_000, STATS_SINCE_CHARGED)); + + clocks.realtime = clocks.uptime = 310; + bi.noteScreenStateLocked(Display.STATE_OFF); + assertEquals(250_000, bi.computeBatteryRealtime(350_000, STATS_SINCE_CHARGED)); + assertEquals(140_000, bi.computeBatteryScreenOffRealtime(350_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getScreenOnTime(350_000, STATS_SINCE_CHARGED)); + assertEquals(0, bi.getScreenDozeTime(350_000, STATS_SINCE_CHARGED)); + + clocks.realtime = clocks.uptime = 400; + bi.noteScreenStateLocked(Display.STATE_DOZE); + assertEquals(400_000, bi.computeBatteryRealtime(500_000, STATS_SINCE_CHARGED)); + assertEquals(290_000, bi.computeBatteryScreenOffRealtime(500_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getScreenOnTime(500_000, STATS_SINCE_CHARGED)); + assertEquals(100_000, bi.getScreenDozeTime(500_000, STATS_SINCE_CHARGED)); + + clocks.realtime = clocks.uptime = 1000; + bi.noteScreenStateLocked(Display.STATE_OFF); + assertEquals(1400_000, bi.computeBatteryRealtime(1500_000, STATS_SINCE_CHARGED)); + assertEquals(1290_000, bi.computeBatteryScreenOffRealtime(1500_000, STATS_SINCE_CHARGED)); + assertEquals(110_000, bi.getScreenOnTime(1500_000, STATS_SINCE_CHARGED)); + assertEquals(600_000, bi.getScreenDozeTime(1500_000, STATS_SINCE_CHARGED)); + } + } diff --git a/media/java/android/media/tv/ITvInputHardware.aidl b/media/java/android/media/tv/ITvInputHardware.aidl index 96223ba7bac1..94c1013a837e 100644 --- a/media/java/android/media/tv/ITvInputHardware.aidl +++ b/media/java/android/media/tv/ITvInputHardware.aidl @@ -40,12 +40,6 @@ interface ITvInputHardware { void setStreamVolume(float volume); /** - * Dispatch key event to HDMI service. The events would be automatically converted to - * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail. - */ - boolean dispatchKeyEventToHdmi(in KeyEvent event); - - /** * Override default audio sink from audio policy. When override is on, it is * TvInputService's responsibility to adjust to audio configuration change * (for example, when the audio sink becomes unavailable or more desirable diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index d7a9edefa3f2..fd1f2cf66b79 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -2590,12 +2590,9 @@ public final class TvInputManager { } } + /** @removed */ public boolean dispatchKeyEventToHdmi(KeyEvent event) { - try { - return mInterface.dispatchKeyEventToHdmi(event); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + return false; } public void overrideAudioSink(int audioType, String audioAddress, int samplingRate, diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 7f6980de576a..28827e64cf96 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -24,7 +24,7 @@ #include <media/IMediaHTTPService.h> #include <media/MediaPlayerInterface.h> #include <media/MediaAnalyticsItem.h> -#include <media/stagefright/Utils.h> // for FOURCC definition +#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition #include <stdio.h> #include <assert.h> #include <limits.h> diff --git a/packages/PrintSpooler/tests/outofprocess/Android.mk b/packages/PrintSpooler/tests/outofprocess/Android.mk index 3c02453c78a1..149be743dbbb 100644 --- a/packages/PrintSpooler/tests/outofprocess/Android.mk +++ b/packages/PrintSpooler/tests/outofprocess/Android.mk @@ -21,7 +21,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := android.test.runner -LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4 +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4 print-test-util-lib LOCAL_PACKAGE_NAME := PrintSpoolerOutOfProcessTests LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml index 4a05f6f52a9a..307cc936a567 100644 --- a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml +++ b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml @@ -21,10 +21,12 @@ <application> <uses-library android:name="android.test.runner" /> - <activity android:name=".PrintTestActivity"/> + <activity + android:name="android.print.test.PrintDocumentActivity" + android:theme="@style/NoAnimation" /> <service - android:name=".mockservice.MockPrintService" + android:name="android.print.test.services.FirstPrintService" android:permission="android.permission.BIND_PRINT_SERVICE"> <intent-filter> @@ -37,13 +39,15 @@ </service> <activity - android:name=".mockservice.SettingsActivity" + android:name="android.print.test.services.SettingsActivity" android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY" + android:theme="@style/NoAnimation" android:exported="true"> </activity> <activity - android:name=".mockservice.AddPrintersActivity" + android:name="android.print.test.services.AddPrintersActivity" + android:theme="@style/NoAnimation" android:exported="true"> </activity> diff --git a/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml b/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml new file mode 100644 index 000000000000..49eb2574da02 --- /dev/null +++ b/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml @@ -0,0 +1,22 @@ +<?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. + --> +<resources> + <style name="NoAnimation" parent="@android:style/Theme.DeviceDefault"> + <item name="android:windowAnimationStyle">@null</item> + </style> +</resources> diff --git a/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml b/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml index 9eecf4562bb4..a6282b1c5bb4 100644 --- a/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml +++ b/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml @@ -17,4 +17,4 @@ --> <print-service xmlns:android="http://schemas.android.com/apk/res/android" - android:addPrintersActivity="com.android.printspooler.outofprocess.tests.mockservice.AddPrintersActivity" /> + android:addPrintersActivity="android.print.test.services.AddPrintersActivity" /> diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java deleted file mode 100644 index 9a7f362decb2..000000000000 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2016 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.printspooler.outofprocess.tests; - -import static android.content.pm.PackageManager.GET_META_DATA; -import static android.content.pm.PackageManager.GET_SERVICES; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doCallRealMethod; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.app.Instrumentation; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.CancellationSignal; -import android.os.ParcelFileDescriptor; -import android.print.PrintAttributes; -import android.print.PrintDocumentAdapter; -import android.print.PrintManager; -import android.print.PrinterId; -import android.printservice.CustomPrinterIconCallback; -import android.printservice.PrintJob; -import android.printservice.PrintService; -import android.provider.Settings; -import android.support.test.InstrumentationRegistry; -import android.support.test.rule.ActivityTestRule; -import android.support.test.uiautomator.UiDevice; - -import com.android.printspooler.outofprocess.tests.mockservice.PrintServiceCallbacks; -import com.android.printspooler.outofprocess.tests.mockservice.PrinterDiscoverySessionCallbacks; -import com.android.printspooler.outofprocess.tests.mockservice.StubbablePrinterDiscoverySession; - -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Rule; -import org.mockito.stubbing.Answer; - -import java.io.FileInputStream; -import java.io.IOException; -import java.util.List; - -/** - * This is the base class for print tests. - */ -abstract class BasePrintTest { - protected static final long OPERATION_TIMEOUT = 30000; - private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success"; - private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT - private static String sDisabledPrintServicesBefore; - - private android.print.PrintJob mPrintJob; - - private static Instrumentation sInstrumentation; - private static UiDevice sUiDevice; - - @Rule - public ActivityTestRule<PrintTestActivity> mActivityRule = - new ActivityTestRule<>(PrintTestActivity.class, false, true); - - /** - * Return the UI device - * - * @return the UI device - */ - public UiDevice getUiDevice() { - return sUiDevice; - } - - protected static Instrumentation getInstrumentation() { - return sInstrumentation; - } - - @BeforeClass - public static void setUpClass() throws Exception { - sInstrumentation = InstrumentationRegistry.getInstrumentation(); - assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature( - PackageManager.FEATURE_PRINTING)); - - sUiDevice = UiDevice.getInstance(sInstrumentation); - - // Make sure we start with a clean slate. - clearPrintSpoolerData(); - - disablePrintServices(sInstrumentation.getTargetContext().getPackageName()); - - // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2 - // Dexmaker is used by mockito. - System.setProperty("dexmaker.dexcache", getInstrumentation() - .getTargetContext().getCacheDir().getPath()); - } - - @AfterClass - public static void tearDownClass() throws Exception { - enablePrintServices(); - } - - @Before - public void unlockScreen() throws Exception { - // Unlock screen. - runShellCommand("input keyevent KEYCODE_WAKEUP"); - runShellCommand("wm dismiss-keyguard"); - } - - @After - public void exitActivities() throws Exception { - // Exit print spooler - getUiDevice().pressBack(); - getUiDevice().pressBack(); - } - - protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter, - final PrintAttributes attributes) { - // Initiate printing as if coming from the app. - getInstrumentation().runOnMainSync(() -> { - PrintManager printManager = (PrintManager) getActivity() - .getSystemService(Context.PRINT_SERVICE); - mPrintJob = printManager.print("Print job", adapter, attributes); - }); - - return mPrintJob; - } - - protected PrintTestActivity getActivity() { - return mActivityRule.getActivity(); - } - - public static String runShellCommand(String cmd) - throws IOException { - ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(cmd); - byte[] buf = new byte[512]; - int bytesRead; - FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); - StringBuilder stdout = new StringBuilder(); - while ((bytesRead = fis.read(buf)) != -1) { - stdout.append(new String(buf, 0, bytesRead)); - } - fis.close(); - return stdout.toString(); - } - - protected static void clearPrintSpoolerData() throws Exception { - assertTrue("failed to clear print spooler data", runShellCommand( - String.format("pm clear --user %d %s", CURRENT_USER_ID, - PrintManager.PRINT_SPOOLER_PACKAGE_NAME)).contains( - PM_CLEAR_SUCCESS_OUTPUT)); - } - - /** - * Disable all print services beside the ones we want to leave enabled. - * - * @param packageToLeaveEnabled The package of the services to leave enabled. - */ - private static void disablePrintServices(String packageToLeaveEnabled) throws IOException { - Instrumentation instrumentation = getInstrumentation(); - - sDisabledPrintServicesBefore = runShellCommand( - "settings get secure " + Settings.Secure.DISABLED_PRINT_SERVICES); - - Intent printServiceIntent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE); - List<ResolveInfo> installedServices = instrumentation.getContext().getPackageManager() - .queryIntentServices(printServiceIntent, GET_SERVICES | GET_META_DATA); - - StringBuilder builder = new StringBuilder(); - for (ResolveInfo service : installedServices) { - if (packageToLeaveEnabled.equals(service.serviceInfo.packageName)) { - continue; - } - if (builder.length() > 0) { - builder.append(":"); - } - builder.append(new ComponentName(service.serviceInfo.packageName, - service.serviceInfo.name).flattenToString()); - } - - runShellCommand( - "settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " " + builder); - } - - /** - * Revert {@link #disablePrintServices(String)} - */ - private static void enablePrintServices() throws IOException { - runShellCommand("settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " " - + sDisabledPrintServicesBefore); - } - - @SuppressWarnings("unchecked") - protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks( - Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery, - Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking, - Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking, - Answer<Void> onDestroy) { - PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class); - - doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class)); - when(callbacks.getSession()).thenCallRealMethod(); - - if (onStartPrinterDiscovery != null) { - doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery( - any(List.class)); - } - if (onStopPrinterDiscovery != null) { - doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery(); - } - if (onValidatePrinters != null) { - doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters( - any(List.class)); - } - if (onStartPrinterStateTracking != null) { - doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking( - any(PrinterId.class)); - } - if (onRequestCustomPrinterIcon != null) { - doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon( - any(PrinterId.class), any(CancellationSignal.class), - any(CustomPrinterIconCallback.class)); - } - if (onStopPrinterStateTracking != null) { - doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking( - any(PrinterId.class)); - } - if (onDestroy != null) { - doAnswer(onDestroy).when(callbacks).onDestroy(); - } - - return callbacks; - } - - protected PrintServiceCallbacks createMockPrintServiceCallbacks( - Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks, - Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) { - final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class); - - doCallRealMethod().when(service).setService(any(PrintService.class)); - when(service.getService()).thenCallRealMethod(); - - if (onCreatePrinterDiscoverySessionCallbacks != null) { - doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service) - .onCreatePrinterDiscoverySessionCallbacks(); - } - if (onPrintJobQueued != null) { - doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class)); - } - if (onRequestCancelPrintJob != null) { - doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob( - any(PrintJob.class)); - } - - return service; - } -} diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java deleted file mode 100644 index 4905a0b5b5fa..000000000000 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2016 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.printspooler.outofprocess.tests; - -import android.app.Activity; -import android.os.Bundle; -import android.view.WindowManager; - -public class PrintTestActivity extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON - | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); - } -} diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java index 78a0cac623ba..7ebf93d8f4ed 100644 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java +++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java @@ -30,6 +30,11 @@ import android.print.PrinterCapabilitiesInfo; import android.print.PrinterId; import android.print.PrinterInfo; import android.print.pdf.PrintedPdfDocument; +import android.print.test.BasePrintTest; +import android.print.test.services.AddPrintersActivity; +import android.print.test.services.FirstPrintService; +import android.print.test.services.PrinterDiscoverySessionCallbacks; +import android.print.test.services.StubbablePrinterDiscoverySession; import android.support.test.filters.LargeTest; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject; @@ -38,11 +43,6 @@ import android.support.test.uiautomator.UiSelector; import android.support.test.uiautomator.Until; import android.util.Log; -import com.android.printspooler.outofprocess.tests.mockservice.AddPrintersActivity; -import com.android.printspooler.outofprocess.tests.mockservice.MockPrintService; -import com.android.printspooler.outofprocess.tests.mockservice.PrinterDiscoverySessionCallbacks; -import com.android.printspooler.outofprocess.tests.mockservice.StubbablePrinterDiscoverySession; - import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -62,10 +62,6 @@ import java.util.function.Supplier; public class WorkflowTest extends BasePrintTest { private static final String LOG_TAG = WorkflowTest.class.getSimpleName(); - private static float sWindowAnimationScaleBefore; - private static float sTransitionAnimationScaleBefore; - private static float sAnimatiorDurationScaleBefore; - private PrintAttributes.MediaSize mFirst; private boolean mSelectPrinter; private PrintAttributes.MediaSize mSecond; @@ -91,7 +87,7 @@ public class WorkflowTest extends BasePrintTest { throws TimeoutException, InterruptedException { long startTime = System.currentTimeMillis(); while (condition.get()) { - long timeLeft = OPERATION_TIMEOUT - (System.currentTimeMillis() - startTime); + long timeLeft = OPERATION_TIMEOUT_MILLIS - (System.currentTimeMillis() - startTime); if (timeLeft < 0) { throw new TimeoutException(); } @@ -156,7 +152,7 @@ public class WorkflowTest extends BasePrintTest { */ private void setMockPrintServiceCallbacks(StubbablePrinterDiscoverySession[] sessionRef, ArrayList<String> trackedPrinters, PrintAttributes.MediaSize mediaSize) { - MockPrintService.setCallbacks(createMockPrintServiceCallbacks( + FirstPrintService.setCallbacks(createMockPrintServiceCallbacks( inv -> createMockPrinterDiscoverySessionCallbacks(inv2 -> { synchronized (sessionRef) { sessionRef[0] = ((PrinterDiscoverySessionCallbacks) inv2.getMock()) @@ -243,7 +239,7 @@ public class WorkflowTest extends BasePrintTest { callback.onWriteFailed(e.getMessage()); } } - }, null); + }, (PrintAttributes) null); } @Parameterized.Parameters @@ -303,7 +299,7 @@ public class WorkflowTest extends BasePrintTest { } else { Log.i(LOG_TAG, "Waiting for error message"); assertNotNull(getUiDevice().wait(Until.findObject( - By.text("This printer isn't available right now.")), OPERATION_TIMEOUT)); + By.text("This printer isn't available right now.")), OPERATION_TIMEOUT_MILLIS)); } setPrinter("All printers\u2026"); @@ -316,7 +312,7 @@ public class WorkflowTest extends BasePrintTest { () -> addPrinter(session[0], "2nd printer", mSecond)); // This executes the observer registered above - clickOn(new UiSelector().text(MockPrintService.class.getCanonicalName()) + clickOn(new UiSelector().text(FirstPrintService.class.getCanonicalName()) .resourceId("com.android.printspooler:id/title")); getUiDevice().pressBack(); @@ -342,7 +338,8 @@ public class WorkflowTest extends BasePrintTest { } else { Log.i(LOG_TAG, "Waiting for error message"); assertNotNull(getUiDevice().wait(Until.findObject( - By.text("This printer isn't available right now.")), OPERATION_TIMEOUT)); + By.text("This printer isn't available right now.")), + OPERATION_TIMEOUT_MILLIS)); } Log.i(LOG_TAG, "Waiting for 1st printer to be not tracked"); @@ -370,7 +367,8 @@ public class WorkflowTest extends BasePrintTest { } else { Log.i(LOG_TAG, "Waiting for error message"); assertNotNull(getUiDevice().wait(Until.findObject( - By.text("This printer isn't available right now.")), OPERATION_TIMEOUT)); + By.text("This printer isn't available right now.")), + OPERATION_TIMEOUT_MILLIS)); } Log.i(LOG_TAG, "Waiting for 1st printer to be tracked"); diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java deleted file mode 100644 index 2ea4e7dcdbe2..000000000000 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice; - -import android.app.Activity; -import android.os.Bundle; -import android.support.annotation.NonNull; - -import java.util.ArrayList; - -public class AddPrintersActivity extends Activity { - private static final ArrayList<Runnable> sObservers = new ArrayList<>(); - - public static void addObserver(@NonNull Runnable observer) { - synchronized (sObservers) { - sObservers.add(observer); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - synchronized (sObservers) { - for (Runnable sObserver : sObservers) { - sObserver.run(); - } - } - - finish(); - } - - public static void clearObservers() { - synchronized (sObservers) { - sObservers.clear(); - } - } -} diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java deleted file mode 100644 index 3a231130c5c4..000000000000 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice; - -public class MockPrintService extends StubbablePrintService { - - private static final Object sLock = new Object(); - - private static PrintServiceCallbacks sCallbacks; - - public static void setCallbacks(PrintServiceCallbacks callbacks) { - synchronized (sLock) { - sCallbacks = callbacks; - } - } - - @Override - protected PrintServiceCallbacks getCallbacks() { - synchronized (sLock) { - if (sCallbacks != null) { - sCallbacks.setService(this); - } - return sCallbacks; - } - } -} diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java deleted file mode 100644 index 07baa0fe1191..000000000000 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice; - -import android.printservice.PrintJob; -import android.printservice.PrintService; - -public abstract class PrintServiceCallbacks { - - private PrintService mService; - - public PrintService getService() { - return mService; - } - - public void setService(PrintService service) { - mService = service; - } - - public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks(); - - public abstract void onRequestCancelPrintJob(PrintJob printJob); - - public abstract void onPrintJobQueued(PrintJob printJob); -} diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java deleted file mode 100644 index 5c1260c37dc4..000000000000 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice; - -import android.os.CancellationSignal; -import android.print.PrinterId; -import android.printservice.CustomPrinterIconCallback; - -import java.util.List; - -public abstract class PrinterDiscoverySessionCallbacks { - - private StubbablePrinterDiscoverySession mSession; - - public void setSession(StubbablePrinterDiscoverySession session) { - mSession = session; - } - - public StubbablePrinterDiscoverySession getSession() { - return mSession; - } - - public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList); - - public abstract void onStopPrinterDiscovery(); - - public abstract void onValidatePrinters(List<PrinterId> printerIds); - - public abstract void onStartPrinterStateTracking(PrinterId printerId); - - public abstract void onRequestCustomPrinterIcon(PrinterId printerId, - CancellationSignal cancellationSignal, CustomPrinterIconCallback callback); - - public abstract void onStopPrinterStateTracking(PrinterId printerId); - - public abstract void onDestroy(); -} diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java deleted file mode 100644 index be9d19b4d634..000000000000 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice; - -import android.printservice.PrintJob; -import android.printservice.PrintService; -import android.printservice.PrinterDiscoverySession; - -public abstract class StubbablePrintService extends PrintService { - - @Override - public PrinterDiscoverySession onCreatePrinterDiscoverySession() { - PrintServiceCallbacks callbacks = getCallbacks(); - if (callbacks != null) { - return new StubbablePrinterDiscoverySession(this, - getCallbacks().onCreatePrinterDiscoverySessionCallbacks()); - } - return null; - } - - @Override - public void onRequestCancelPrintJob(PrintJob printJob) { - PrintServiceCallbacks callbacks = getCallbacks(); - if (callbacks != null) { - callbacks.onRequestCancelPrintJob(printJob); - } - } - - @Override - public void onPrintJobQueued(PrintJob printJob) { - PrintServiceCallbacks callbacks = getCallbacks(); - if (callbacks != null) { - callbacks.onPrintJobQueued(printJob); - } - } - - protected abstract PrintServiceCallbacks getCallbacks(); -} diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java deleted file mode 100644 index a828cd6b39db..000000000000 --- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2016 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.printspooler.outofprocess.tests.mockservice; - -import android.os.CancellationSignal; -import android.print.PrinterId; -import android.printservice.CustomPrinterIconCallback; -import android.printservice.PrintService; -import android.printservice.PrinterDiscoverySession; -import android.support.annotation.NonNull; - -import java.util.List; - -public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession { - private final PrintService mService; - private final PrinterDiscoverySessionCallbacks mCallbacks; - - public StubbablePrinterDiscoverySession(PrintService service, - PrinterDiscoverySessionCallbacks callbacks) { - mService = service; - mCallbacks = callbacks; - if (mCallbacks != null) { - mCallbacks.setSession(this); - } - } - - public PrintService getService() { - return mService; - } - - @Override - public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) { - if (mCallbacks != null) { - mCallbacks.onStartPrinterDiscovery(priorityList); - } - } - - @Override - public void onStopPrinterDiscovery() { - if (mCallbacks != null) { - mCallbacks.onStopPrinterDiscovery(); - } - } - - @Override - public void onValidatePrinters(@NonNull List<PrinterId> printerIds) { - if (mCallbacks != null) { - mCallbacks.onValidatePrinters(printerIds); - } - } - - @Override - public void onStartPrinterStateTracking(@NonNull PrinterId printerId) { - if (mCallbacks != null) { - mCallbacks.onStartPrinterStateTracking(printerId); - } - } - - @Override - public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId, - @NonNull CancellationSignal cancellationSignal, - @NonNull CustomPrinterIconCallback callback) { - if (mCallbacks != null) { - mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback); - } - } - - @Override - public void onStopPrinterStateTracking(@NonNull PrinterId printerId) { - if (mCallbacks != null) { - mCallbacks.onStopPrinterStateTracking(printerId); - } - } - - @Override - public void onDestroy() { - if (mCallbacks != null) { - mCallbacks.onDestroy(); - } - } -} diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index b72869518c45..3d083b124df2 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -983,4 +983,14 @@ <!-- [CHAR LIMIT=NONE] Dialog body explaining that the app just selected by the user will not work after a reboot until until after the user enters their credentials, such as a PIN or password. --> <string name="direct_boot_unaware_dialog_message">Note: After a reboot, this app can\'t start until you unlock your phone</string> + <!--Label of IMS registration header --> + <string name="ims_reg_title">"IMS registration state"</string> + <!--Used when IMS registration state is registered --> + <string name="ims_reg_status_registered">"Registered"</string> + <!--Used when IMS registration state is not registered --> + <string name="ims_reg_status_not_registered">"Not registered"</string> + + <!-- About phone, status item value if the actual value is not available. --> + <string name="status_unavailable">Unavailable</string> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java index 7998b2ef872b..f79be7eaddb1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java @@ -33,21 +33,27 @@ public abstract class AbstractLogdSizePreferenceController extends + "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED"; public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE"; + @VisibleForTesting + static final String LOW_RAM_CONFIG_PROPERTY_KEY = "ro.config.low_ram"; private static final String SELECT_LOGD_SIZE_KEY = "select_logd_size"; @VisibleForTesting static final String SELECT_LOGD_SIZE_PROPERTY = "persist.logd.size"; static final String SELECT_LOGD_TAG_PROPERTY = "persist.log.tag"; // Tricky, isLoggable only checks for first character, assumes silence static final String SELECT_LOGD_TAG_SILENCE = "Settings"; - private static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log"; + @VisibleForTesting + static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log"; private static final String SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY = "log.tag.snet_event_log"; private static final String SELECT_LOGD_DEFAULT_SIZE_PROPERTY = "ro.logd.size"; @VisibleForTesting static final String SELECT_LOGD_DEFAULT_SIZE_VALUE = "262144"; private static final String SELECT_LOGD_SVELTE_DEFAULT_SIZE_VALUE = "65536"; // 32768 is merely a menu marker, 64K is our lowest log buffer size we replace it with. - private static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536"; + @VisibleForTesting + static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536"; static final String SELECT_LOGD_OFF_SIZE_MARKER_VALUE = "32768"; + @VisibleForTesting + static final String DEFAULT_SNET_TAG = "I"; private ListPreference mLogdSize; @@ -154,7 +160,7 @@ public abstract class AbstractLogdSizePreferenceController extends if ((snetValue == null) || (snetValue.length() == 0)) { snetValue = SystemProperties.get(SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY); if ((snetValue == null) || (snetValue.length() == 0)) { - SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, "I"); + SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, DEFAULT_SNET_TAG); } } // Silence all log sources, security logs notwithstanding diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java new file mode 100644 index 000000000000..ba358f83c9d5 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java @@ -0,0 +1,85 @@ +/* + * 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.settingslib.deviceinfo; + +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.text.TextUtils; + +import com.android.settingslib.R; +import com.android.settingslib.core.lifecycle.Lifecycle; + +/** + * Preference controller for bluetooth address + */ +public abstract class AbstractBluetoothAddressPreferenceController + extends AbstractConnectivityPreferenceController { + + @VisibleForTesting + static final String KEY_BT_ADDRESS = "bt_address"; + + private static final String[] CONNECTIVITY_INTENTS = { + BluetoothAdapter.ACTION_STATE_CHANGED + }; + + private Preference mBtAddress; + + public AbstractBluetoothAddressPreferenceController(Context context, Lifecycle lifecycle) { + super(context, lifecycle); + } + + @Override + public boolean isAvailable() { + return BluetoothAdapter.getDefaultAdapter() != null; + } + + @Override + public String getPreferenceKey() { + return KEY_BT_ADDRESS; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mBtAddress = screen.findPreference(KEY_BT_ADDRESS); + updateConnectivity(); + } + + @Override + protected String[] getConnectivityIntents() { + return CONNECTIVITY_INTENTS; + } + + @SuppressLint("HardwareIds") + @Override + protected void updateConnectivity() { + BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter(); + if (bluetooth != null && mBtAddress != null) { + String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null; + if (!TextUtils.isEmpty(address)) { + // Convert the address to lowercase for consistency with the wifi MAC address. + mBtAddress.setSummary(address.toLowerCase()); + } else { + mBtAddress.setSummary(R.string.status_unavailable); + } + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java new file mode 100644 index 000000000000..c6552f77a2b2 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java @@ -0,0 +1,114 @@ +/* + * 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.settingslib.deviceinfo; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.Message; + +import com.android.internal.util.ArrayUtils; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +import java.lang.ref.WeakReference; + +/** + * Base class for preference controllers which listen to connectivity broadcasts + */ +public abstract class AbstractConnectivityPreferenceController + extends AbstractPreferenceController implements LifecycleObserver, OnStart, OnStop { + + private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (ArrayUtils.contains(getConnectivityIntents(), action)) { + getHandler().sendEmptyMessage(EVENT_UPDATE_CONNECTIVITY); + } + } + }; + + private static final int EVENT_UPDATE_CONNECTIVITY = 600; + + private Handler mHandler; + + public AbstractConnectivityPreferenceController(Context context, Lifecycle lifecycle) { + super(context); + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public void onStop() { + mContext.unregisterReceiver(mConnectivityReceiver); + } + + @Override + public void onStart() { + final IntentFilter connectivityIntentFilter = new IntentFilter(); + final String[] intents = getConnectivityIntents(); + for (String intent : intents) { + connectivityIntentFilter.addAction(intent); + } + + mContext.registerReceiver(mConnectivityReceiver, connectivityIntentFilter, + android.Manifest.permission.CHANGE_NETWORK_STATE, null); + } + + protected abstract String[] getConnectivityIntents(); + + protected abstract void updateConnectivity(); + + private Handler getHandler() { + if (mHandler == null) { + mHandler = new ConnectivityEventHandler(this); + } + return mHandler; + } + + private static class ConnectivityEventHandler extends Handler { + private WeakReference<AbstractConnectivityPreferenceController> mPreferenceController; + + public ConnectivityEventHandler(AbstractConnectivityPreferenceController activity) { + mPreferenceController = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + AbstractConnectivityPreferenceController preferenceController + = mPreferenceController.get(); + if (preferenceController == null) { + return; + } + + switch (msg.what) { + case EVENT_UPDATE_CONNECTIVITY: + preferenceController.updateConnectivity(); + break; + default: + throw new IllegalStateException("Unknown message " + msg.what); + } + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java new file mode 100644 index 000000000000..bb8404b0abd5 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java @@ -0,0 +1,95 @@ +/* + * 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.settingslib.deviceinfo; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.os.PersistableBundle; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; + +import com.android.settingslib.R; +import com.android.settingslib.core.lifecycle.Lifecycle; + +/** + * Preference controller for IMS status + */ +public abstract class AbstractImsStatusPreferenceController + extends AbstractConnectivityPreferenceController { + + @VisibleForTesting + static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state"; + + private static final String[] CONNECTIVITY_INTENTS = { + BluetoothAdapter.ACTION_STATE_CHANGED, + ConnectivityManager.CONNECTIVITY_ACTION, + WifiManager.LINK_CONFIGURATION_CHANGED_ACTION, + WifiManager.NETWORK_STATE_CHANGED_ACTION, + }; + + private Preference mImsStatus; + + public AbstractImsStatusPreferenceController(Context context, + Lifecycle lifecycle) { + super(context, lifecycle); + } + + @Override + public boolean isAvailable() { + CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class); + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + PersistableBundle config = null; + if (configManager != null) { + config = configManager.getConfigForSubId(subId); + } + return config != null && config.getBoolean( + CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL); + } + + @Override + public String getPreferenceKey() { + return KEY_IMS_REGISTRATION_STATE; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mImsStatus = screen.findPreference(KEY_IMS_REGISTRATION_STATE); + updateConnectivity(); + } + + @Override + protected String[] getConnectivityIntents() { + return CONNECTIVITY_INTENTS; + } + + @Override + protected void updateConnectivity() { + int subId = SubscriptionManager.getDefaultDataSubscriptionId(); + if (mImsStatus != null) { + TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); + mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ? + R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered); + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java new file mode 100644 index 000000000000..ded30226e2ae --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java @@ -0,0 +1,112 @@ +/* + * 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.settingslib.deviceinfo; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.wifi.WifiManager; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settingslib.R; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import java.net.InetAddress; +import java.util.Iterator; + +/** + * Preference controller for IP address + */ +public abstract class AbstractIpAddressPreferenceController + extends AbstractConnectivityPreferenceController { + + @VisibleForTesting + static final String KEY_IP_ADDRESS = "wifi_ip_address"; + + private static final String[] CONNECTIVITY_INTENTS = { + ConnectivityManager.CONNECTIVITY_ACTION, + WifiManager.LINK_CONFIGURATION_CHANGED_ACTION, + WifiManager.NETWORK_STATE_CHANGED_ACTION, + }; + + private Preference mIpAddress; + private final ConnectivityManager mCM; + + public AbstractIpAddressPreferenceController(Context context, Lifecycle lifecycle) { + super(context, lifecycle); + mCM = context.getSystemService(ConnectivityManager.class); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_IP_ADDRESS; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mIpAddress = screen.findPreference(KEY_IP_ADDRESS); + updateConnectivity(); + } + + @Override + protected String[] getConnectivityIntents() { + return CONNECTIVITY_INTENTS; + } + + @Override + protected void updateConnectivity() { + String ipAddress = getDefaultIpAddresses(mCM); + if (ipAddress != null) { + mIpAddress.setSummary(ipAddress); + } else { + mIpAddress.setSummary(R.string.status_unavailable); + } + } + + /** + * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style + * addresses. + * @param cm ConnectivityManager + * @return the formatted and newline-separated IP addresses, or null if none. + */ + private static String getDefaultIpAddresses(ConnectivityManager cm) { + LinkProperties prop = cm.getActiveLinkProperties(); + return formatIpAddresses(prop); + } + + private static String formatIpAddresses(LinkProperties prop) { + if (prop == null) return null; + Iterator<InetAddress> iter = prop.getAllAddresses().iterator(); + // If there are no entries, return null + if (!iter.hasNext()) return null; + // Concatenate all available addresses, newline separated + StringBuilder addresses = new StringBuilder(); + while (iter.hasNext()) { + addresses.append(iter.next().getHostAddress()); + if (iter.hasNext()) addresses.append("\n"); + } + return addresses.toString(); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java new file mode 100644 index 000000000000..ac61ade19222 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java @@ -0,0 +1,119 @@ +/* + * 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.settingslib.deviceinfo; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.text.format.DateUtils; + +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +import java.lang.ref.WeakReference; + +/** + * Preference controller for uptime + */ +public abstract class AbstractUptimePreferenceController extends AbstractPreferenceController + implements LifecycleObserver, OnStart, OnStop { + + @VisibleForTesting + static final String KEY_UPTIME = "up_time"; + private static final int EVENT_UPDATE_STATS = 500; + + private Preference mUptime; + private Handler mHandler; + + public AbstractUptimePreferenceController(Context context, Lifecycle lifecycle) { + super(context); + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public void onStart() { + getHandler().sendEmptyMessage(EVENT_UPDATE_STATS); + } + + @Override + public void onStop() { + getHandler().removeMessages(EVENT_UPDATE_STATS); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_UPTIME; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mUptime = screen.findPreference(KEY_UPTIME); + updateTimes(); + } + + private Handler getHandler() { + if (mHandler == null) { + mHandler = new MyHandler(this); + } + return mHandler; + } + + private void updateTimes() { + mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime())); + } + + private static class MyHandler extends Handler { + private WeakReference<AbstractUptimePreferenceController> mStatus; + + public MyHandler(AbstractUptimePreferenceController activity) { + mStatus = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + AbstractUptimePreferenceController status = mStatus.get(); + if (status == null) { + return; + } + + switch (msg.what) { + case EVENT_UPDATE_STATS: + status.updateTimes(); + sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000); + break; + + default: + throw new IllegalStateException("Unknown message " + msg.what); + } + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java new file mode 100644 index 000000000000..d57b64f0c0cb --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java @@ -0,0 +1,88 @@ +/* + * 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.settingslib.deviceinfo; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.text.TextUtils; + +import com.android.settingslib.R; +import com.android.settingslib.core.lifecycle.Lifecycle; + +/** + * Preference controller for WIFI MAC address + */ +public abstract class AbstractWifiMacAddressPreferenceController + extends AbstractConnectivityPreferenceController { + + @VisibleForTesting + static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address"; + + private static final String[] CONNECTIVITY_INTENTS = { + ConnectivityManager.CONNECTIVITY_ACTION, + WifiManager.LINK_CONFIGURATION_CHANGED_ACTION, + WifiManager.NETWORK_STATE_CHANGED_ACTION, + }; + + private Preference mWifiMacAddress; + private final WifiManager mWifiManager; + + public AbstractWifiMacAddressPreferenceController(Context context, Lifecycle lifecycle) { + super(context, lifecycle); + mWifiManager = context.getSystemService(WifiManager.class); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY_WIFI_MAC_ADDRESS; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS); + updateConnectivity(); + } + + @Override + protected String[] getConnectivityIntents() { + return CONNECTIVITY_INTENTS; + } + + @SuppressLint("HardwareIds") + @Override + protected void updateConnectivity() { + WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress(); + if (!TextUtils.isEmpty(macAddress)) { + mWifiMacAddress.setSummary(macAddress); + } else { + mWifiMacAddress.setSummary(R.string.status_unavailable); + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java index 56b84415e907..9c347631d817 100644 --- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java +++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java @@ -261,7 +261,7 @@ public class SuggestionParser { if (requiredAccountType == null) { return true; } - AccountManager accountManager = AccountManager.get(mContext); + AccountManager accountManager = mContext.getSystemService(AccountManager.class); Account[] accounts = accountManager.getAccountsByType(requiredAccountType); boolean satisfiesRequiredAccount = accounts.length > 0; if (!satisfiesRequiredAccount) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 12455d85588b..c56e1da14921 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -291,15 +291,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } /** - * Force a scan for wifi networks to happen now. - */ - public void forceScan() { - if (mWifiManager.isWifiEnabled() && mScanner != null) { - mScanner.forceScan(); - } - } - - /** * Temporarily stop scanning for wifi networks. */ public void pauseScanning() { @@ -789,14 +780,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } - public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved, - boolean includeScans) { - WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans); - tracker.forceUpdate(); - tracker.copyAndNotifyListeners(false /*notifyListeners*/); - return tracker.getAccessPoints(); - } - @VisibleForTesting final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override @@ -986,11 +969,6 @@ public class WifiTracker implements LifecycleObserver, OnStart, OnStop, OnDestro } } - void forceScan() { - removeMessages(MSG_SCAN); - sendEmptyMessage(MSG_SCAN); - } - void pause() { mRetry = 0; removeMessages(MSG_SCAN); diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk index 55b635ec9b24..bc1a834535a6 100644 --- a/packages/SettingsLib/tests/robotests/Android.mk +++ b/packages/SettingsLib/tests/robotests/Android.mk @@ -42,11 +42,12 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) # Include the testing libraries (JUnit4 + Robolectric libs). LOCAL_STATIC_JAVA_LIBRARIES := \ mockito-robolectric-prebuilt \ + platform-robolectric-android-all-stubs \ truth-prebuilt LOCAL_JAVA_LIBRARIES := \ junit \ - platform-robolectric-prebuilt + platform-robolectric-3.4.2-prebuilt LOCAL_INSTRUMENTATION_FOR := SettingsLibShell LOCAL_MODULE := SettingsLibRoboTests @@ -69,4 +70,6 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ LOCAL_TEST_PACKAGE := SettingsLibShell -include prebuilts/misc/common/robolectric/run_robotests.mk +LOCAL_ROBOTEST_TIMEOUT := 36000 + +include prebuilts/misc/common/robolectric/3.4.2/run_robotests.mk diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java index c3a505ac67f4..ca366ea4b6cc 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java @@ -20,8 +20,11 @@ import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NO import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT; import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; + import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; + import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doReturn; @@ -56,10 +59,10 @@ public class RestrictedLockUtilsTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private RestrictedLockUtils.Proxy mProxy; - private static final int mUserId = 194; - private static final int mProfileId = 160; - private static final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class"); - private static final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class"); + private final int mUserId = 194; + private final int mProfileId = 160; + private final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class"); + private final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class"); @Before public void setUp() { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java index 8099eb11edb4..698e44247e73 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java @@ -53,15 +53,15 @@ public class SettingsLibRobolectricTestRunner extends RobolectricTestRunner { static void getIncludedResourcePaths(String packageName, List<ResourcePath> paths) { paths.add(new ResourcePath( - packageName, + null, Fs.fileFromPath("./frameworks/base/packages/SettingsLib/res"), null)); paths.add(new ResourcePath( - packageName, + null, Fs.fileFromPath("./frameworks/base/core/res/res"), null)); paths.add(new ResourcePath( - packageName, + null, Fs.fileFromPath("./frameworks/support/v7/appcompat/res"), null)); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java index 31abecda9a39..3af976836439 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java @@ -17,7 +17,7 @@ package com.android.settingslib; public class TestConfig { - public static final int SDK_VERSION = 23; + public static final int SDK_VERSION = 25; public static final String MANIFEST_PATH = "frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml"; } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java index d6bca0e27b3b..1290391e3ce2 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java @@ -35,9 +35,9 @@ import com.android.settingslib.core.lifecycle.events.OnStop; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; +import org.robolectric.android.controller.ActivityController; +import org.robolectric.android.controller.FragmentController; import org.robolectric.annotation.Config; -import org.robolectric.util.ActivityController; -import org.robolectric.util.FragmentController; import static com.google.common.truth.Truth.assertThat; @@ -168,7 +168,7 @@ public class LifecycleTest { Robolectric.buildFragment(TestDialogFragment.class); TestDialogFragment fragment = fragmentController.get(); - fragmentController.attach().create().start().resume(); + fragmentController.create().start().resume(); fragment.onCreateOptionsMenu(null, null); fragment.onPrepareOptionsMenu(null); fragment.onOptionsItemSelected(null); @@ -192,7 +192,7 @@ public class LifecycleTest { Robolectric.buildFragment(TestFragment.class); TestFragment fragment = fragmentController.get(); - fragmentController.attach().create().start().resume(); + fragmentController.create().start().resume(); fragment.onCreateOptionsMenu(null, null); fragment.onPrepareOptionsMenu(null); fragment.onOptionsItemSelected(null); @@ -235,7 +235,7 @@ public class LifecycleTest { OptionItemAccepter accepter = new OptionItemAccepter(); fragment.getLifecycle().addObserver(accepter); - fragmentController.attach().create().start().resume(); + fragmentController.create().start().resume(); fragment.onCreateOptionsMenu(null, null); fragment.onPrepareOptionsMenu(null); fragment.onOptionsItemSelected(null); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java index 94bfd8f22de2..26d357018a75 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java @@ -16,9 +16,29 @@ package com.android.settingslib.development; +import static com.android.settingslib.development.AbstractLogdSizePreferenceController + .DEFAULT_SNET_TAG; +import static com.android.settingslib.development.AbstractLogdSizePreferenceController + .LOW_RAM_CONFIG_PROPERTY_KEY; +import static com.android.settingslib.development.AbstractLogdSizePreferenceController + .SELECT_LOGD_MINIMUM_SIZE_VALUE; +import static com.android.settingslib.development.AbstractLogdSizePreferenceController + .SELECT_LOGD_OFF_SIZE_MARKER_VALUE; +import static com.android.settingslib.development.AbstractLogdSizePreferenceController + .SELECT_LOGD_SIZE_PROPERTY; +import static com.android.settingslib.development.AbstractLogdSizePreferenceController + .SELECT_LOGD_SNET_TAG_PROPERTY; +import static com.android.settingslib.development.AbstractLogdSizePreferenceController + .SELECT_LOGD_TAG_PROPERTY; +import static com.android.settingslib.development.AbstractLogdSizePreferenceController + .SELECT_LOGD_TAG_SILENCE; + +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; +import android.content.Context; import android.os.SystemProperties; import android.support.v7.preference.ListPreference; import android.support.v7.preference.PreferenceScreen; @@ -27,6 +47,7 @@ import com.android.settingslib.R; import com.android.settingslib.SettingsLibRobolectricTestRunner; import com.android.settingslib.TestConfig; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,22 +66,43 @@ public class LogdSizePreferenceControllerTest { @Mock private PreferenceScreen mPreferenceScreen; + /** + * List Values + * + * 0: off + * 1: 64k + * 2: 256k + * 3: 1M + * 4: 4M + * 5: 16M + */ + private String[] mListValues; + private String[] mListSummaries; + private Context mContext; private AbstractLogdSizePreferenceController mController; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mController = new AbstractLogdSizePreferenceController(RuntimeEnvironment.application) {}; - + mContext = RuntimeEnvironment.application; + mController = new AbstractLogdSizePreferenceController(RuntimeEnvironment.application) { + }; + mListValues = mContext.getResources().getStringArray(R.array.select_logd_size_values); + mListSummaries = mContext.getResources().getStringArray(R.array.select_logd_size_summaries); doReturn(mListPreference).when(mPreferenceScreen) .findPreference(mController.getPreferenceKey()); mController.displayPreference(mPreferenceScreen); } + @After + public void tearDown() { + SystemPropertiesTestImpl.clear(); + } + @Test - public void testUpateLogdSizeValues_lowRamEntries() { - SystemProperties.set("ro.config.low_ram", "true"); + public void testUpdateLogdSizeValues_lowRamEntries() { + SystemProperties.set(LOW_RAM_CONFIG_PROPERTY_KEY, "true"); mController.updateLogdSizeValues(); verify(mListPreference).setEntries(R.array.select_logd_size_lowram_titles); } @@ -77,4 +119,79 @@ public class LogdSizePreferenceControllerTest { verify(mListPreference).setValue( AbstractLogdSizePreferenceController.SELECT_LOGD_OFF_SIZE_MARKER_VALUE); } + + @Test + public void onPreferenceChange_noTagsSizeValueOff_shouldSetTagAndSnetTagAndSet64KSize() { + mController.onPreferenceChange(mListPreference, SELECT_LOGD_OFF_SIZE_MARKER_VALUE); + + final String tag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY); + final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY); + final String snetTag = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY); + + assertThat(tag).isEqualTo(SELECT_LOGD_TAG_SILENCE); + assertThat(logSize).isEqualTo(SELECT_LOGD_MINIMUM_SIZE_VALUE); + assertThat(snetTag).isEqualTo(DEFAULT_SNET_TAG); + } + + @Test + public void onPreferenceChange_noTagsSizeValue64K_shouldNotSetTagAndSet64KSize() { + mController.onPreferenceChange(mListPreference, SELECT_LOGD_MINIMUM_SIZE_VALUE); + + final String tag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY); + final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY); + final String snetTag = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY); + + assertThat(tag).isEmpty(); + assertThat(logSize).isEqualTo(SELECT_LOGD_MINIMUM_SIZE_VALUE); + assertThat(snetTag).isEmpty(); + } + + @Test + public void onPreferenceChange_set1M_shouldUpdateSettingLogSizeTo1M() { + mController.onPreferenceChange(mListPreference, mListValues[3]); + + final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY); + + assertThat(logSize).isEqualTo(mListValues[3]); + } + + @Test + public void onPreferenceChange_noValue_shouldUpdateSettingToEmpty() { + mController.onPreferenceChange(mListPreference, "" /* new value */); + + final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY); + + assertThat(logSize).isEmpty(); + } + + @Test + public void updateLogdSizeValues_noValueSet_shouldSetDefaultTo64K() { + SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, "" /* new value */); + + mController.updateLogdSizeValues(); + + verify(mListPreference).setValue(mListValues[2]); + verify(mListPreference).setSummary(mListSummaries[2]); + } + + @Test + public void updateLogdSizeValues_noValueSetLowRamSet_shouldSetDefaultTo64K() { + SystemProperties.set(LOW_RAM_CONFIG_PROPERTY_KEY, Boolean.toString(true)); + SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, "" /* new value */); + + mController.updateLogdSizeValues(); + + verify(mListPreference).setValue(mListValues[1]); + verify(mListPreference).setSummary(mListSummaries[1]); + } + + @Test + public void updateLogdSizeValues_64KSet_shouldSet64K() { + SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, mListValues[1]); + + mController.updateLogdSizeValues(); + + verify(mListPreference).setValue(mListValues[1]); + verify(mListPreference).setSummary(mListSummaries[1]); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java index 2f89d86984f7..6977e09180c5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java @@ -54,4 +54,8 @@ public class SystemPropertiesTestImpl extends ShadowSystemProperties { public static void set(String key, String val) { sProperties.put(key, val); } + + public static synchronized void clear() { + sProperties.clear(); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java new file mode 100644 index 000000000000..1de7a7a9fb39 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java @@ -0,0 +1,107 @@ +/* + * 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.settingslib.deviceinfo; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.Mockito.doReturn; + +import android.bluetooth.BluetoothAdapter; +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class BluetoothAddressPreferenceControllerTest { + @Mock + private Context mContext; + @Mock + private Lifecycle mLifecycle; + @Mock + private PreferenceScreen mScreen; + @Mock + private Preference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + doReturn(mPreference).when(mScreen) + .findPreference(AbstractBluetoothAddressPreferenceController.KEY_BT_ADDRESS); + } + + @Implements(BluetoothAdapter.class) + public static class ShadowEmptyBluetoothAdapter { + @Implementation + public static BluetoothAdapter getDefaultAdapter() { + return null; + } + } + + @Test + @Config(shadows = ShadowEmptyBluetoothAdapter.class) + public void testNoBluetooth() { + final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController = + new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle); + + assertWithMessage("Should not show pref if no bluetooth") + .that(bluetoothAddressPreferenceController.isAvailable()) + .isFalse(); + } + + @Test + public void testHasBluetooth() { + final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController = + new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle); + + assertWithMessage("Should show pref if bluetooth is present") + .that(bluetoothAddressPreferenceController.isAvailable()) + .isTrue(); + } + + @Test + public void testHasBluetoothStateChangedFilter() { + final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController = + new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle); + + assertWithMessage("Filter should have BluetoothAdapter.ACTION_STATE_CHANGED") + .that(bluetoothAddressPreferenceController.getConnectivityIntents()) + .asList().contains(BluetoothAdapter.ACTION_STATE_CHANGED); + } + + private static class ConcreteBluetoothAddressPreferenceController + extends AbstractBluetoothAddressPreferenceController { + + public ConcreteBluetoothAddressPreferenceController(Context context, + Lifecycle lifecycle) { + super(context, lifecycle); + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java new file mode 100644 index 000000000000..362dbd944525 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java @@ -0,0 +1,123 @@ +/* + * 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.settingslib.deviceinfo; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; +import android.os.Handler; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ConnectivityPreferenceControllerTest { + @Mock + private Context mContext; + + @Mock + private Lifecycle mLifecycle; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testBroadcastReceiver() { + final AbstractConnectivityPreferenceController preferenceController = + spy(new ConcreteConnectivityPreferenceController(mContext, mLifecycle)); + + final ArgumentCaptor<BroadcastReceiver> receiverArgumentCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + final ArgumentCaptor<IntentFilter> filterArgumentCaptor = + ArgumentCaptor.forClass(IntentFilter.class); + + doReturn(new String[] {"Filter1", "Filter2"}) + .when(preferenceController).getConnectivityIntents(); + + preferenceController.onStart(); + + verify(mContext, times(1)) + .registerReceiver(receiverArgumentCaptor.capture(), + filterArgumentCaptor.capture(), + anyString(), nullable(Handler.class)); + + final BroadcastReceiver receiver = receiverArgumentCaptor.getValue(); + final IntentFilter filter = filterArgumentCaptor.getValue(); + + assertWithMessage("intent filter should match 'Filter1'") + .that(filter.matchAction("Filter1")) + .isTrue(); + assertWithMessage("intent filter should match 'Filter2'") + .that(filter.matchAction("Filter2")) + .isTrue(); + + preferenceController.onStop(); + + verify(mContext, times(1)).unregisterReceiver(receiver); + } + + private static class ConcreteConnectivityPreferenceController + extends AbstractConnectivityPreferenceController { + + + public ConcreteConnectivityPreferenceController(Context context, + Lifecycle lifecycle) { + super(context, lifecycle); + } + + @Override + public boolean isAvailable() { + return false; + } + + @Override + public String getPreferenceKey() { + return null; + } + + @Override + protected String[] getConnectivityIntents() { + return new String[0]; + } + + @Override + protected void updateConnectivity() { + + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java new file mode 100644 index 000000000000..112ee64a6468 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java @@ -0,0 +1,112 @@ +/* + * 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.settingslib.deviceinfo; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.content.Context; +import android.os.PersistableBundle; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ImsStatusPreferenceControllerTest { + @Mock + private Context mContext; + @Mock + private Lifecycle mLifecycle; + @Mock + private PreferenceScreen mScreen; + @Mock + private Preference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + doReturn(mPreference).when(mScreen) + .findPreference(AbstractImsStatusPreferenceController.KEY_IMS_REGISTRATION_STATE); + } + + @Test + @Config(shadows = ShadowSubscriptionManager.class) + public void testIsAvailable() { + CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class); + doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class); + + PersistableBundle config = new PersistableBundle(1); + config.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true); + doReturn(config).when(carrierConfigManager).getConfigForSubId(anyInt()); + + final AbstractImsStatusPreferenceController imsStatusPreferenceController = + new ConcreteImsStatusPreferenceController(mContext, mLifecycle); + + assertWithMessage("Should be available when IMS registration is true").that( + imsStatusPreferenceController.isAvailable()).isTrue(); + + config.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false); + + assertWithMessage("Should not be available when IMS registration is false") + .that(imsStatusPreferenceController.isAvailable()).isFalse(); + + doReturn(null).when(carrierConfigManager).getConfigForSubId(anyInt()); + + assertWithMessage("Should not be available when IMS registration is false") + .that(imsStatusPreferenceController.isAvailable()).isFalse(); + + doReturn(null).when(mContext).getSystemService(CarrierConfigManager.class); + + assertWithMessage("Should not be available when CarrierConfigManager service is null") + .that(imsStatusPreferenceController.isAvailable()).isFalse(); + } + + @Implements(SubscriptionManager.class) + public static class ShadowSubscriptionManager { + @Implementation + public static int getDefaultDataSubscriptionId() { + return 1234; + } + } + + private static class ConcreteImsStatusPreferenceController + extends AbstractImsStatusPreferenceController { + + public ConcreteImsStatusPreferenceController(Context context, + Lifecycle lifecycle) { + super(context, lifecycle); + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java new file mode 100644 index 000000000000..d0ecae37c849 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java @@ -0,0 +1,85 @@ +/* + * 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.settingslib.deviceinfo; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.Mockito.doReturn; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.wifi.WifiManager; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import java.util.Arrays; +import java.util.List; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class IpAddressPreferenceControllerTest { + @Mock + private Context mContext; + @Mock + private Lifecycle mLifecycle; + @Mock + private PreferenceScreen mScreen; + @Mock + private Preference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + doReturn(mPreference).when(mScreen) + .findPreference(AbstractIpAddressPreferenceController.KEY_IP_ADDRESS); + } + + @Test + public void testHasIntentFilters() { + final AbstractIpAddressPreferenceController ipAddressPreferenceController = + new ConcreteIpAddressPreferenceController(mContext, mLifecycle); + final List<String> expectedIntents = Arrays.asList( + ConnectivityManager.CONNECTIVITY_ACTION, + WifiManager.LINK_CONFIGURATION_CHANGED_ACTION, + WifiManager.NETWORK_STATE_CHANGED_ACTION); + + + assertWithMessage("Intent filter should contain expected intents") + .that(ipAddressPreferenceController.getConnectivityIntents()) + .asList().containsAllIn(expectedIntents); + } + + private static class ConcreteIpAddressPreferenceController extends + AbstractIpAddressPreferenceController { + + public ConcreteIpAddressPreferenceController(Context context, + Lifecycle lifecycle) { + super(context, lifecycle); + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java new file mode 100644 index 000000000000..f68533ba4081 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java @@ -0,0 +1,102 @@ +/* + * 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.settingslib.deviceinfo; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.os.SystemClock; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.text.format.DateUtils; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowLooper; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class UptimePreferenceControllerTest { + @Mock + private Context mContext; + @Mock + private Lifecycle mLifecycle; + @Mock + private PreferenceScreen mScreen; + @Mock + private Preference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + doReturn(mPreference).when(mScreen) + .findPreference(AbstractUptimePreferenceController.KEY_UPTIME); + } + + @Test + public void testDisplayPreference() { + final AbstractUptimePreferenceController uptimePreferenceController = + new ConcreteUptimePreferenceController(mContext, mLifecycle); + + uptimePreferenceController.displayPreference(mScreen); + + // SystemClock is shadowed so it shouldn't advance unexpectedly while the test is running + verify(mPreference).setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime())); + } + + @Test + public void testUptimeTick() { + final AbstractUptimePreferenceController uptimePreferenceController = + new ConcreteUptimePreferenceController(mContext, null /* lifecycle */); + + uptimePreferenceController.displayPreference(mScreen); + + verify(mPreference, times(1)).setSummary(any(CharSequence.class)); + + uptimePreferenceController.onStart(); + + verify(mPreference, times(2)).setSummary(any(CharSequence.class)); + + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + + verify(mPreference, times(3)).setSummary(any(CharSequence.class)); + + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + + verify(mPreference, times(4)).setSummary(any(CharSequence.class)); + } + + private static class ConcreteUptimePreferenceController + extends AbstractUptimePreferenceController { + public ConcreteUptimePreferenceController(Context context, + Lifecycle lifecycle) { + super(context, lifecycle); + } + } + +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java new file mode 100644 index 000000000000..265a60b3325a --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java @@ -0,0 +1,114 @@ +/* + * 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.settingslib.deviceinfo; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settingslib.R; +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.TestConfig; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; + +import java.util.Arrays; +import java.util.List; + +@SuppressLint("HardwareIds") +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class WifiMacAddressPreferenceControllerTest { + @Mock + private Context mContext; + @Mock + private Lifecycle mLifecycle; + @Mock + private PreferenceScreen mScreen; + @Mock + private Preference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + doReturn(mPreference).when(mScreen) + .findPreference(AbstractWifiMacAddressPreferenceController.KEY_WIFI_MAC_ADDRESS); + } + + @Test + public void testHasIntentFilters() { + final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController = + new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle); + final List<String> expectedIntents = Arrays.asList( + ConnectivityManager.CONNECTIVITY_ACTION, + WifiManager.LINK_CONFIGURATION_CHANGED_ACTION, + WifiManager.NETWORK_STATE_CHANGED_ACTION); + + + assertWithMessage("Intent filter should contain expected intents") + .that(wifiMacAddressPreferenceController.getConnectivityIntents()) + .asList().containsAllIn(expectedIntents); + } + + @Test + public void testWifiMacAddress() { + final WifiManager wifiManager = mock(WifiManager.class); + final WifiInfo wifiInfo = mock(WifiInfo.class); + doReturn("00:11:22:33:44:55").when(wifiInfo).getMacAddress(); + + doReturn(null).when(wifiManager).getConnectionInfo(); + doReturn(wifiManager).when(mContext).getSystemService(WifiManager.class); + + final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController = + new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle); + + wifiMacAddressPreferenceController.displayPreference(mScreen); + + verify(mPreference).setSummary(R.string.status_unavailable); + + doReturn(wifiInfo).when(wifiManager).getConnectionInfo(); + + wifiMacAddressPreferenceController.displayPreference(mScreen); + + verify(mPreference).setSummary("00:11:22:33:44:55"); + } + + private static class ConcreteWifiMacAddressPreferenceController + extends AbstractWifiMacAddressPreferenceController { + + public ConcreteWifiMacAddressPreferenceController(Context context, + Lifecycle lifecycle) { + super(context, lifecycle); + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java index 3e90435b21d7..dad3a28f3638 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java @@ -29,6 +29,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.RuntimeEnvironment.application; +import static org.robolectric.shadow.api.Shadow.extract; import android.app.ActivityManager; import android.content.ContentResolver; @@ -66,7 +67,6 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; -import org.robolectric.internal.ShadowExtractor; import java.util.ArrayList; import java.util.Collections; @@ -79,7 +79,6 @@ import java.util.Map; shadows = {TileUtilsTest.TileUtilsShadowRemoteViews.class}) public class TileUtilsTest { - @Mock private Context mContext; @Mock private PackageManager mPackageManager; @@ -97,6 +96,7 @@ public class TileUtilsTest { @Before public void setUp() throws NameNotFoundException { + mContext = spy(application); MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getResourcesForApplication(anyString())).thenReturn(mResources); @@ -456,8 +456,7 @@ public class TileUtilsTest { assertThat(tile.remoteViews).isNotNull(); assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference); // Make sure the summary TextView got a new text string. - TileUtilsShadowRemoteViews shadowRemoteViews = - (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews); + TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews); assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary); assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text"); } @@ -494,8 +493,7 @@ public class TileUtilsTest { assertThat(tile.remoteViews).isNotNull(); assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference); // Make sure the summary TextView didn't get any text view updates. - TileUtilsShadowRemoteViews shadowRemoteViews = - (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews); + TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews); assertThat(shadowRemoteViews.overrideViewId).isNull(); assertThat(shadowRemoteViews.overrideText).isNull(); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java index f31d2e1afcd8..db599a776d23 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java @@ -18,8 +18,11 @@ package com.android.settingslib.suggestions; import static com.google.common.truth.Truth.assertThat; +import static org.robolectric.RuntimeEnvironment.application; +import static org.robolectric.shadow.api.Shadow.extract; + +import android.app.ApplicationPackageManager; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ResolveInfo; @@ -34,22 +37,21 @@ import com.android.settingslib.drawer.TileUtilsTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.res.ResourceLoader; -import org.robolectric.res.builder.DefaultPackageManager; -import org.robolectric.res.builder.RobolectricPackageManager; +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowApplicationPackageManager; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @RunWith(SettingsLibRobolectricTestRunner.class) -@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, + shadows = SuggestionParserTest.TestPackageManager.class) public class SuggestionParserTest { - private Context mContext; - private RobolectricPackageManager mPackageManager; + private TestPackageManager mPackageManager; private SuggestionParser mSuggestionParser; private SuggestionCategory mMultipleCategory; private SuggestionCategory mExclusiveCategory; @@ -61,11 +63,8 @@ public class SuggestionParserTest { @Before public void setUp() { - RuntimeEnvironment.setRobolectricPackageManager( - new TestPackageManager(RuntimeEnvironment.getAppResourceLoader())); - mContext = RuntimeEnvironment.application; - mPackageManager = RuntimeEnvironment.getRobolectricPackageManager(); - mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); + mPackageManager = extract(application.getPackageManager()); + mPrefs = PreferenceManager.getDefaultSharedPreferences(application); mSuggestion = new Tile(); mSuggestion.intent = new Intent("action"); mSuggestion.intent.setComponent(new ComponentName("pkg", "cls")); @@ -81,7 +80,7 @@ public class SuggestionParserTest { mExpiredExclusiveCategory.exclusive = true; mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0; - mSuggestionParser = new SuggestionParser(mContext, mPrefs, + mSuggestionParser = new SuggestionParser(application, mPrefs, Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory), "0"); @@ -199,7 +198,7 @@ public class SuggestionParserTest { final Tile suggestion = mSuggestionsBeforeDismiss.get(0); if (mSuggestionParser.dismissSuggestion(suggestion)) { - RuntimeEnvironment.getRobolectricPackageManager().removeResolveInfosForIntent( + mPackageManager.removeResolveInfosForIntent( new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category), suggestion.intent.getComponent().getPackageName()); } @@ -207,13 +206,10 @@ public class SuggestionParserTest { mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled); } - private static class TestPackageManager extends DefaultPackageManager { - - TestPackageManager(ResourceLoader appResourceLoader) { - super(appResourceLoader); - } + @Implements(ApplicationPackageManager.class) + public static class TestPackageManager extends ShadowApplicationPackageManager { - @Override + @Implementation public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) { return super.queryIntentActivities(intent, flags); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java index a376dcdbbbd3..b53cc371c641 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java @@ -16,7 +16,7 @@ package com.android.settingslib.testutils.shadow; -import static org.robolectric.internal.Shadow.directlyOn; +import static org.robolectric.shadow.api.Shadow.directlyOn; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java index 1f633da4644a..95ff13b45fb9 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java @@ -29,6 +29,9 @@ public interface GlobalActions extends Plugin { default void showShutdownUi(boolean isReboot, String reason) { } + default void destroy() { + } + @ProvidesInterface(version = GlobalActionsManager.VERSION) public interface GlobalActionsManager { int VERSION = 1; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index dc626fb4df65..851b78cfcd49 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -84,9 +84,6 @@ public class DozeUi implements DozeMachine.Part { case DOZE_REQUEST_PULSE: pulseWhileDozing(mMachine.getPulseReason()); break; - case DOZE_PULSE_DONE: - mHost.abortPulsing(); - break; case INITIALIZED: mHost.startDozing(); break; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java index 09a08f09fc9a..f06cda0f787a 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java @@ -31,6 +31,7 @@ import android.os.ServiceManager; public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager { + private GlobalActions mPlugin; private Extension<GlobalActions> mExtension; private IStatusBarService mBarService; @@ -41,10 +42,19 @@ public class GlobalActionsComponent extends SystemUI implements Callbacks, Globa mExtension = Dependency.get(ExtensionController.class).newExtension(GlobalActions.class) .withPlugin(GlobalActions.class) .withDefault(() -> new GlobalActionsImpl(mContext)) + .withCallback(this::onExtensionCallback) .build(); + mPlugin = mExtension.get(); SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this); } + private void onExtensionCallback(GlobalActions newPlugin) { + if (mPlugin != null) { + mPlugin.destroy(); + } + mPlugin = newPlugin; + } + @Override public void handleShowShutdownUi(boolean isReboot, String reason) { mExtension.get().showShutdownUi(isReboot, reason); diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 189badfc42fc..d82f9cd44e4a 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -16,27 +16,6 @@ package com.android.systemui.globalactions; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; -import com.android.internal.R; -import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.colorextraction.ColorExtractor.GradientColors; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.util.EmergencyAffordanceManager; -import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.TelephonyProperties; -import com.android.internal.widget.LockPatternUtils; -import com.android.systemui.Dependency; -import com.android.systemui.HardwareUiLayout; -import com.android.systemui.Interpolators; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; -import com.android.systemui.statusbar.notification.NotificationUtils; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator; -import com.android.systemui.volume.VolumeDialogMotion.LogDecelerateInterpolator; - -import android.animation.ValueAnimator; -import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.ActivityManager; import android.app.Dialog; import android.app.WallpaperManager; @@ -69,7 +48,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; -import android.util.MathUtils; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; @@ -86,7 +64,23 @@ import android.widget.ImageView.ScaleType; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.internal.R; +import com.android.internal.colorextraction.ColorExtractor; +import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.colorextraction.drawable.GradientDrawable; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.telephony.TelephonyIntents; +import com.android.internal.telephony.TelephonyProperties; +import com.android.internal.util.EmergencyAffordanceManager; +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.Dependency; +import com.android.systemui.HardwareUiLayout; +import com.android.systemui.Interpolators; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator; import java.util.ArrayList; import java.util.List; @@ -96,7 +90,8 @@ import java.util.List; * may show depending on whether the keyguard is showing, and whether the device * is provisioned. */ -class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener { +class GlobalActionsDialog implements DialogInterface.OnDismissListener, + DialogInterface.OnClickListener { static public final String SYSTEM_DIALOG_REASON_KEY = "reason"; static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; @@ -195,6 +190,14 @@ class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogIn } } + /** + * Dismiss the global actions dialog, if it's currently shown + */ + public void dismissDialog() { + mHandler.removeMessages(MESSAGE_DISMISS); + mHandler.sendEmptyMessage(MESSAGE_DISMISS); + } + private void awakenIfNecessary() { if (mDreamManager != null) { try { diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 2cf230c8e12d..35634374d5dd 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -14,12 +14,12 @@ package com.android.systemui.globalactions; +import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS; + import android.app.Dialog; import android.app.KeyguardManager; -import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.Context; -import android.graphics.Color; import android.graphics.Point; import android.view.ViewGroup; import android.view.Window; @@ -32,12 +32,14 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.colorextraction.drawable.GradientDrawable; import com.android.settingslib.Utils; import com.android.systemui.Dependency; +import com.android.systemui.SysUiServiceProvider; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.GlobalActions; +import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardMonitor; -public class GlobalActionsImpl implements GlobalActions { +public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks { private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f; @@ -45,15 +47,23 @@ public class GlobalActionsImpl implements GlobalActions { private final KeyguardMonitor mKeyguardMonitor; private final DeviceProvisionedController mDeviceProvisionedController; private GlobalActionsDialog mGlobalActions; + private boolean mDisabled; public GlobalActionsImpl(Context context) { mContext = context; mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); + SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this); + } + + @Override + public void destroy() { + SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this); } @Override public void showGlobalActions(GlobalActionsManager manager) { + if (mDisabled) return; if (mGlobalActions == null) { mGlobalActions = new GlobalActionsDialog(mContext, manager); } @@ -107,4 +117,14 @@ public class GlobalActionsImpl implements GlobalActions { d.show(); } + + @Override + public void disable(int state1, int state2, boolean animate) { + final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0; + if (disabled == mDisabled) return; + mDisabled = disabled; + if (disabled && mGlobalActions != null) { + mGlobalActions.dismissDialog(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java index b83590979ef7..5ec3dff0043c 100644 --- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java +++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java @@ -23,7 +23,7 @@ import android.os.Handler; */ public class DelayedWakeLock implements WakeLock { - private static final long RELEASE_DELAY_MS = 100; + private static final long RELEASE_DELAY_MS = 120; private final Handler mHandler; private final WakeLock mInner; diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index e17a6a6aefa9..4834f3893240 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4665,6 +4665,9 @@ message MetricsEvent { // OS: P DIALOG_CLEAR_ADB_KEYS = 1223; + // Open: Settings > Developer options > Quick setting tile config + DEVELOPMENT_QS_TILE_CONFIG = 1224; + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 0b2dd9b8d5e3..26401a7bbf9f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -12939,6 +12939,10 @@ public class ActivityManagerService extends IActivityManager.Stub throw new IllegalArgumentException("Provided bugreport type is not correct, value: " + bugreportType); } + // Always log caller, even if it does not have permission to dump. + String type = extraOptions == null ? "bugreport" : extraOptions; + Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid()); + enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport"); if (extraOptions != null) { SystemProperties.set("dumpstate.options", extraOptions); diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 5583e86cde70..d7cd81ff3c5f 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1371,6 +1371,7 @@ public class Tethering extends BaseNetworkObserver { sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS); } } + mUpstreamNetworkMonitor.setCurrentUpstream((ns != null) ? ns.network : null); setUpstreamNetwork(ns); } diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index c5f752807cb7..b35ed75106b3 100644 --- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -95,7 +95,10 @@ public class UpstreamNetworkMonitor { private NetworkCallback mDefaultNetworkCallback; private NetworkCallback mMobileNetworkCallback; private boolean mDunRequired; - private Network mCurrentDefault; + // The current system default network (not really used yet). + private Network mDefaultInternetNetwork; + // The current upstream network used for tethering. + private Network mTetheringUpstreamNetwork; public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) { mContext = ctx; @@ -130,10 +133,12 @@ public class UpstreamNetworkMonitor { releaseCallback(mDefaultNetworkCallback); mDefaultNetworkCallback = null; + mDefaultInternetNetwork = null; releaseCallback(mListenAllCallback); mListenAllCallback = null; + mTetheringUpstreamNetwork = null; mNetworkMap.clear(); } @@ -207,7 +212,7 @@ public class UpstreamNetworkMonitor { break; default: /* If we've found an active upstream connection that's not DUN/HIPRI - * we should stop any outstanding DUN/HIPRI start requests. + * we should stop any outstanding DUN/HIPRI requests. * * If we found NONE we don't want to do this as we want any previous * requests to keep trying to bring up something we can use. @@ -219,6 +224,10 @@ public class UpstreamNetworkMonitor { return typeStatePair.ns; } + public void setCurrentUpstream(Network upstream) { + mTetheringUpstreamNetwork = upstream; + } + public Set<IpPrefix> getLocalPrefixes() { return (Set<IpPrefix>) mLocalPrefixes.clone(); } @@ -250,7 +259,7 @@ public class UpstreamNetworkMonitor { // These request*() calls can be deleted post oag/339444. return; } - mCurrentDefault = network; + mDefaultInternetNetwork = network; break; case CALLBACK_MOBILE_REQUEST: @@ -302,6 +311,13 @@ public class UpstreamNetworkMonitor { network, newNc)); } + // Log changes in upstream network signal strength, if available. + if (network.equals(mTetheringUpstreamNetwork) && newNc.hasSignalStrength()) { + final int newSignal = newNc.getSignalStrength(); + final String prevSignal = getSignalStrength(prev.networkCapabilities); + mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal); + } + mNetworkMap.put(network, new NetworkState( null, prev.linkProperties, newNc, network, null, null)); // TODO: If sufficient information is available to select a more @@ -330,9 +346,21 @@ public class UpstreamNetworkMonitor { notifyTarget(EVENT_ON_LINKPROPERTIES, network); } + private void handleSuspended(int callbackType, Network network) { + if (callbackType != CALLBACK_LISTEN_ALL) return; + if (!network.equals(mTetheringUpstreamNetwork)) return; + mLog.log("SUSPENDED current upstream: " + network); + } + + private void handleResumed(int callbackType, Network network) { + if (callbackType != CALLBACK_LISTEN_ALL) return; + if (!network.equals(mTetheringUpstreamNetwork)) return; + mLog.log("RESUMED current upstream: " + network); + } + private void handleLost(int callbackType, Network network) { if (callbackType == CALLBACK_TRACK_DEFAULT) { - mCurrentDefault = null; + mDefaultInternetNetwork = null; // Receiving onLost() for a default network does not necessarily // mean the network is gone. We wait for a separate notification // on either the LISTEN_ALL or MOBILE_REQUEST callbacks before @@ -401,8 +429,15 @@ public class UpstreamNetworkMonitor { recomputeLocalPrefixes(); } - // TODO: Handle onNetworkSuspended(); - // TODO: Handle onNetworkResumed(); + @Override + public void onNetworkSuspended(Network network) { + handleSuspended(mCallbackType, network); + } + + @Override + public void onNetworkResumed(Network network) { + handleResumed(mCallbackType, network); + } @Override public void onLost(Network network) { @@ -467,4 +502,9 @@ public class UpstreamNetworkMonitor { return prefixSet; } + + private static String getSignalStrength(NetworkCapabilities nc) { + if (nc == null || !nc.hasSignalStrength()) return "unknown"; + return Integer.toString(nc.getSignalStrength()); + } } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 9aabdab7daa8..9a6e609445a5 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -29,6 +29,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; +import android.os.Trace; import android.text.format.DateUtils; import android.util.EventLog; import android.util.MathUtils; @@ -304,6 +305,7 @@ class AutomaticBrightnessController { } private void handleLightSensorEvent(long time, float lux) { + Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux); mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX); if (mAmbientLightRingBuffer.size() == 0) { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index f8e58362e7ae..f930b523e626 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -683,8 +683,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Configure auto-brightness. boolean autoBrightnessEnabled = false; if (mAutomaticBrightnessController != null) { - final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig - && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND); + final boolean autoBrightnessEnabledInDoze = + mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); autoBrightnessEnabled = mPowerRequest.useAutoBrightness && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) && brightness < 0; @@ -726,8 +726,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } // Use default brightness when dozing unless overridden. - if (brightness < 0 && (state == Display.STATE_DOZE - || state == Display.STATE_DOZE_SUSPEND)) { + if (brightness < 0 && Display.isDozeState(state)) { brightness = mScreenBrightnessDozeConfig; } @@ -777,7 +776,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Skip the animation when the screen is off or suspended or transition to/from VR. if (!mPendingScreenOff) { if (mSkipScreenOnBrightnessRamp) { - if (state == Display.STATE_ON) { if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) { mInitialAutoBrightness = brightness; @@ -794,15 +792,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - boolean wasOrWillBeInVr = (state == Display.STATE_VR || oldState == Display.STATE_VR); - if ((state == Display.STATE_ON - && mSkipRampState == RAMP_STATE_SKIP_NONE - || state == Display.STATE_DOZE && !mBrightnessBucketsInDozeConfig) - && !wasOrWillBeInVr) { + final boolean wasOrWillBeInVr = + (state == Display.STATE_VR || oldState == Display.STATE_VR); + final boolean initialRampSkip = + state == Display.STATE_ON && mSkipRampState != RAMP_STATE_SKIP_NONE; + // While dozing, sometimes the brightness is split into buckets. Rather than animating + // through the buckets, which is unlikely to be smooth in the first place, just jump + // right to the suggested brightness. + final boolean hasBrightnessBuckets = + Display.isDozeState(state) && mBrightnessBucketsInDozeConfig; + // If the color fade is totally covering the screen then we can change the backlight + // level without it being a noticeable jump since any actual content isn't yet visible. + final boolean isDisplayContentVisible = + mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f; + if (initialRampSkip || hasBrightnessBuckets + || wasOrWillBeInVr || !isDisplayContentVisible) { + animateScreenBrightness(brightness, 0); + } else { animateScreenBrightness(brightness, slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast); - } else { - animateScreenBrightness(brightness, 0); } } @@ -925,6 +933,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } if (!reportOnly) { + Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state); mPowerState.setScreenState(state); // Tell battery stats about the transition. try { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 261ae9018414..d7329dbf0e4c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3365,7 +3365,9 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isUpgrade() { // allow instant applications - return mIsUpgrade; + // The system property allows testing ota flow when upgraded to the same image. + return mIsUpgrade || SystemProperties.getBoolean( + "persist.pm.mock-upgrade", false /* default */); } private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() { diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java index 342ec4b79fed..7a2e630cfdac 100644 --- a/services/core/java/com/android/server/policy/GlobalActions.java +++ b/services/core/java/com/android/server/policy/GlobalActions.java @@ -58,6 +58,9 @@ class GlobalActions implements GlobalActionsListener { public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) { if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned); + if (mStatusBarInternal.isGlobalActionsDisabled()) { + return; + } mKeyguardShowing = keyguardShowing; mDeviceProvisioned = deviceProvisioned; mShowing = true; diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index f1fb3e7b4915..6a5ecfaa7c47 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -20,15 +20,25 @@ import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.content.IntentFilter; import android.os.Binder; +import android.os.Bundle; import android.os.IBinder; import android.os.IStatsCompanionService; import android.os.IStatsManager; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; +import android.os.UserManager; import android.util.Slog; +import java.util.ArrayList; +import java.util.List; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.KernelWakelockReader; import com.android.internal.os.KernelWakelockStats; @@ -53,6 +63,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private final PendingIntent mAnomalyAlarmIntent; private final PendingIntent mPollingAlarmIntent; + private final BroadcastReceiver mAppUpdateReceiver; public StatsCompanionService(Context context) { super(); @@ -63,8 +74,85 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { new Intent(mContext, AnomalyAlarmReceiver.class), 0); mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(mContext, PollingAlarmReceiver.class), 0); + mAppUpdateReceiver = new AppUpdateReceiver(); + Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED."); } + private final static int[] toIntArray(List<Integer> list){ + int[] ret = new int[list.size()]; + for(int i = 0;i < ret.length;i++) { + ret[i] = list.get(i); + } + return ret; + } + + // Assumes that sStatsdLock is held. + private final void informAllUidsLocked(Context context) throws RemoteException { + UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); + PackageManager pm = context.getPackageManager(); + final List<UserInfo> users = um.getUsers(true); + if (DEBUG) { + Slog.w(TAG, "Iterating over "+users.size() + " profiles."); + } + + List<Integer> uids = new ArrayList(); + List<Integer> versions = new ArrayList(); + List<String> apps = new ArrayList(); + + // Add in all the apps for every user/profile. + for (UserInfo profile : users) { + List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id); + for (int j = 0; j < pi.size(); j++) { + if (pi.get(j).applicationInfo != null) { + uids.add(pi.get(j).applicationInfo.uid); + versions.add(pi.get(j).versionCode); + apps.add(pi.get(j).packageName); + } + } + } + sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new + String[apps.size()])); + if (DEBUG) { + Slog.w(TAG, "Sent data for "+uids.size() +" apps"); + } + } + + public final static class AppUpdateReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Slog.i(TAG, "StatsCompanionService noticed an app was updated."); + synchronized (sStatsdLock) { + if (sStatsd == null) { + Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing"); + return; + } + try { + if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) { + Bundle b = intent.getExtras(); + int uid = b.getInt(Intent.EXTRA_UID); + boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + if (!replacing) { + // Don't bother sending an update if we're right about to get another + // intent for the new version that's added. + PackageManager pm = context.getPackageManager(); + String app = intent.getData().getSchemeSpecificPart(); + sStatsd.informOnePackageRemoved(app, uid); + } + } else { + PackageManager pm = context.getPackageManager(); + Bundle b = intent.getExtras(); + int uid = b.getInt(Intent.EXTRA_UID); + String app = intent.getData().getSchemeSpecificPart(); + PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER); + sStatsd.informOnePackage(app, uid, pi.versionCode); + } + } catch (Exception e) { + Slog.w(TAG, "Failed to inform statsd of an app update", e); + } + } + } + }; + public final static class AnomalyAlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -275,6 +363,15 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e); forgetEverything(); } + // Setup broadcast receiver for updates + IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED); + filter.addAction(Intent.ACTION_PACKAGE_ADDED); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null, + null); + // Pull the latest state of UID->app name, version mapping when statsd starts. + informAllUidsLocked(mContext); } catch (RemoteException e) { Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e); forgetEverything(); @@ -293,6 +390,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private void forgetEverything() { synchronized (sStatsdLock) { sStatsd = null; + mContext.unregisterReceiver(mAppUpdateReceiver); cancelAnomalyAlarm(); cancelPollingAlarms(); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 0884678478f6..b07fe98d806e 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -77,6 +77,7 @@ public interface StatusBarManagerInternal { void setCurrentUser(int newUserId); + boolean isGlobalActionsDisabled(); void setGlobalActionsListener(GlobalActionsListener listener); void showGlobalActions(); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index bdfbe48153df..c78a3406d0ac 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -16,6 +16,8 @@ package com.android.server.statusbar; +import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS; + import android.app.ActivityThread; import android.app.StatusBarManager; import android.content.ComponentName; @@ -363,6 +365,11 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override + public boolean isGlobalActionsDisabled() { + return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0; + } + + @Override public void setGlobalActionsListener(GlobalActionsListener listener) { mGlobalActionListener = listener; mGlobalActionListener.onStatusBarConnectedChanged(mBar != null); diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 6117da7b1a38..c1607e94dd1e 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -1022,20 +1022,6 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } - @Override - public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException { - synchronized (mImplLock) { - if (mReleased) { - throw new IllegalStateException("Device already released."); - } - } - if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) { - return false; - } - // TODO(hdmi): mHdmiClient.sendKeyEvent(event); - return false; - } - private boolean startCapture(Surface surface, TvStreamConfig config) { synchronized (mImplLock) { if (mReleased) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 0e68a8f64e5f..0b65b2287bdd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -119,6 +119,7 @@ import static com.android.server.wm.proto.DisplayProto.WINDOW_CONTAINER; import android.annotation.CallSuper; import android.annotation.NonNull; import android.app.ActivityManager.StackId; +import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; @@ -3571,6 +3572,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } final int orientation = super.getOrientation(); + boolean isCar = mService.mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE); + if (isCar) { + // In a car, you cannot physically rotate the screen, so it doesn't make sense to + // allow anything but the default orientation. + if (DEBUG_ORIENTATION) Slog.v(TAG_WM, + "Forcing UNSPECIFIED orientation in car. Ignoring " + orientation); + return SCREEN_ORIENTATION_UNSPECIFIED; + } + if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java index 0230f36b6fa0..1925c39e5211 100644 --- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java +++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.system.ErrnoException; import android.system.Os; import android.system.PacketSocketAddress; +import android.text.TextUtils; import android.util.Log; import android.util.LocalLog; @@ -59,11 +60,14 @@ public class ConnectivityPacketTracker { private static final boolean DBG = false; private static final String MARK_START = "--- START ---"; private static final String MARK_STOP = "--- STOP ---"; + private static final String MARK_NAMED_START = "--- START (%s) ---"; + private static final String MARK_NAMED_STOP = "--- STOP (%s) ---"; private final String mTag; private final LocalLog mLog; private final BlockingSocketReader mPacketListener; private boolean mRunning; + private String mDisplayName; public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) { final String ifname; @@ -85,14 +89,16 @@ public class ConnectivityPacketTracker { mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu); } - public void start() { + public void start(String displayName) { mRunning = true; + mDisplayName = displayName; mPacketListener.start(); } public void stop() { mPacketListener.stop(); mRunning = false; + mDisplayName = null; } private final class PacketListener extends BlockingSocketReader { @@ -133,16 +139,19 @@ public class ConnectivityPacketTracker { @Override protected void onStart() { - mLog.log(MARK_START); + final String msg = TextUtils.isEmpty(mDisplayName) + ? MARK_START + : String.format(MARK_NAMED_START, mDisplayName); + mLog.log(msg); } @Override protected void onStop() { - if (mRunning) { - mLog.log(MARK_STOP); - } else { - mLog.log(MARK_STOP + " (packet listener stopped unexpectedly)"); - } + String msg = TextUtils.isEmpty(mDisplayName) + ? MARK_STOP + : String.format(MARK_NAMED_STOP, mDisplayName); + if (!mRunning) msg += " (packet listener stopped unexpectedly)"; + mLog.log(msg); } @Override diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index bc07b8108631..e33f6c99aea8 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -26,6 +26,7 @@ import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties.ProvisioningChange; import android.net.LinkProperties; +import android.net.Network; import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.StaticIpConfiguration; @@ -348,6 +349,16 @@ public class IpManager extends StateMachine { return this; } + public Builder withNetwork(Network network) { + mConfig.mNetwork = network; + return this; + } + + public Builder withDisplayName(String displayName) { + mConfig.mDisplayName = displayName; + return this; + } + public ProvisioningConfiguration build() { return new ProvisioningConfiguration(mConfig); } @@ -362,6 +373,8 @@ public class IpManager extends StateMachine { /* package */ ApfCapabilities mApfCapabilities; /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; + /* package */ Network mNetwork = null; + /* package */ String mDisplayName = null; public ProvisioningConfiguration() {} // used by Builder @@ -374,6 +387,9 @@ public class IpManager extends StateMachine { mStaticIpConfig = other.mStaticIpConfig; mApfCapabilities = other.mApfCapabilities; mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; + mIPv6AddrGenMode = other.mIPv6AddrGenMode; + mNetwork = other.mNetwork; + mDisplayName = other.mDisplayName; } @Override @@ -388,6 +404,8 @@ public class IpManager extends StateMachine { .add("mApfCapabilities: " + mApfCapabilities) .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) + .add("mNetwork: " + mNetwork) + .add("mDisplayName: " + mDisplayName) .toString(); } @@ -1441,10 +1459,10 @@ public class IpManager extends StateMachine { @Override public void enter() { // Get the Configuration for ApfFilter from Context - boolean filter802_3Frames = + final boolean filter802_3Frames = mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames); - int[] ethTypeBlackList = mContext.getResources().getIntArray( + final int[] ethTypeBlackList = mContext.getResources().getIntArray( R.array.config_apfEthTypeBlackList); mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface, @@ -1456,7 +1474,7 @@ public class IpManager extends StateMachine { } mPacketTracker = createPacketTracker(); - if (mPacketTracker != null) mPacketTracker.start(); + if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName); if (mConfiguration.mEnableIPv6 && !startIPv6()) { doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); @@ -1470,7 +1488,7 @@ public class IpManager extends StateMachine { return; } - InitialConfiguration config = mConfiguration.mInitialConfig; + final InitialConfiguration config = mConfiguration.mInitialConfig; if ((config != null) && !applyInitialConfig(config)) { // TODO introduce a new IpManagerEvent constant to distinguish this error case. doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java index 343d237f8cd9..bbd3d13efbd6 100644 --- a/services/net/java/android/net/util/SharedLog.java +++ b/services/net/java/android/net/util/SharedLog.java @@ -106,6 +106,10 @@ public class SharedLog { record(Category.NONE, msg); } + public void logf(String fmt, Object... args) { + log(String.format(fmt, args)); + } + public void mark(String msg) { record(Category.MARK, msg); } 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 1f6dda191d91..cc8bd69eac3f 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -55,10 +55,6 @@ public class ActivityTestsBase { private final Context mContext = InstrumentationRegistry.getContext(); private HandlerThread mHandlerThread; - // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must - // be called at before any tests. - private final WindowManagerService mWms = WindowTestUtils.getWindowManagerService(mContext); - @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -136,7 +132,7 @@ public class ActivityTestsBase { mSupportsSplitScreenMultiWindow = true; mSupportsFreeformWindowManagement = true; mSupportsPictureInPicture = true; - mWindowManager = WindowTestUtils.getWindowManagerService(context); + mWindowManager = WindowTestUtils.getMockWindowManagerService(); } @Override |