diff options
207 files changed, 8200 insertions, 3140 deletions
diff --git a/Android.mk b/Android.mk index 98e4299f40be..6186c55ee87e 100644 --- a/Android.mk +++ b/Android.mk @@ -548,9 +548,10 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ + wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \ wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl \ + wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl \ wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \ - wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ wifi/java/android/net/wifi/rtt/IRttCallback.aidl \ wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \ @@ -1030,6 +1031,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -since $(SRC_API_DIR)/24.txt 24 \ -since $(SRC_API_DIR)/25.txt 25 \ -since $(SRC_API_DIR)/26.txt 26 \ + -since $(SRC_API_DIR)/27.txt 27 \ -werror -lerror -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128 \ -overview $(LOCAL_PATH)/core/java/overview.html \ 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/current.txt b/api/current.txt index d6485cb51c7b..7b5612fe368a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -31834,6 +31834,7 @@ package android.os { field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper"; field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_SMS = "no_sms"; + field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone"; field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer"; @@ -40701,11 +40702,10 @@ package android.telephony.mbms { } public static class DownloadRequest.Builder { - ctor public DownloadRequest.Builder(); + ctor public DownloadRequest.Builder(android.net.Uri); method public android.telephony.mbms.DownloadRequest build(); method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent); method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo); - method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri); method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int); } diff --git a/api/system-current.txt b/api/system-current.txt index 3ad23718a39e..a2863ee1b46e 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); @@ -34678,6 +34677,7 @@ package android.os { field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper"; field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_SMS = "no_sms"; + field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone"; field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer"; @@ -44330,13 +44330,12 @@ package android.telephony.mbms { } public static class DownloadRequest.Builder { - ctor public DownloadRequest.Builder(); + ctor public DownloadRequest.Builder(android.net.Uri); method public android.telephony.mbms.DownloadRequest build(); method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent); method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]); method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String); method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo); - method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri); method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int); } 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/api/test-current.txt b/api/test-current.txt index 6d94ff67eade..834cb362d8c1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -32097,6 +32097,7 @@ package android.os { field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper"; field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_SMS = "no_sms"; + field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone"; field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer"; @@ -41082,11 +41083,10 @@ package android.telephony.mbms { } public static class DownloadRequest.Builder { - ctor public DownloadRequest.Builder(); + ctor public DownloadRequest.Builder(android.net.Uri); method public android.telephony.mbms.DownloadRequest build(); method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent); method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo); - method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri); method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int); } 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/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp index 07a064cf044b..37f6ed710cce 100644 --- a/cmds/incidentd/src/PrivacyBuffer.cpp +++ b/cmds/incidentd/src/PrivacyBuffer.cpp @@ -33,18 +33,18 @@ write_field_or_skip(EncodedBuffer::iterator* iter, EncodedBuffer* buf, uint8_t w { EncodedBuffer::Pointer snapshot = iter->rp()->copy(); size_t bytesToWrite = 0; - uint32_t varint = 0; + uint64_t varint = 0; switch (wireType) { case WIRE_TYPE_VARINT: varint = iter->readRawVarint(); - if(!skip) return buf->writeRawVarint(varint); + if(!skip) return buf->writeRawVarint64(varint); break; case WIRE_TYPE_FIXED64: bytesToWrite = 8; break; case WIRE_TYPE_LENGTH_DELIMITED: bytesToWrite = iter->readRawVarint(); - if(!skip) buf->writeRawVarint(bytesToWrite); + if(!skip) buf->writeRawVarint32(bytesToWrite); break; case WIRE_TYPE_FIXED32: bytesToWrite = 4; @@ -76,7 +76,6 @@ stripField(EncodedBuffer::iterator* iter, EncodedBuffer* buf, const Privacy* par uint8_t wireType = read_wire_type(varint); uint32_t fieldId = read_field_id(varint); const Privacy* policy = parentPolicy->lookup(fieldId); - if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) { bool skip = !spec.CheckPremission(policy); size_t amt = buf->size(); @@ -99,7 +98,7 @@ stripField(EncodedBuffer::iterator* iter, EncodedBuffer* buf, const Privacy* par } buf->writeHeader(fieldId, wireType); - buf->writeRawVarint(finalSize); + buf->writeRawVarint32(finalSize); while (!q.empty()) { EncodedBuffer* subField = q.front(); EncodedBuffer::iterator it = subField->begin(); diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index e7a31a0f8690..4c95007b0c44 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -42,23 +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/StatsPuller.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/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 \ @@ -128,13 +112,21 @@ 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/AnomalyMonitor.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/cmds/statsd/src/AnomalyMonitor.cpp b/cmds/statsd/src/AnomalyMonitor.cpp index 92fe84487d4d..4fbbc7a2267f 100644 --- a/cmds/statsd/src/AnomalyMonitor.cpp +++ b/cmds/statsd/src/AnomalyMonitor.cpp @@ -90,6 +90,36 @@ void AnomalyMonitor::remove(sp<const AnomalyAlarm> alarm) { } } +// More efficient than repeatedly calling remove(mPq.top()) since it batches the +// updates to the registered alarm. +unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> + AnomalyMonitor::popSoonerThan(uint32_t timestampSec) { + + if (DEBUG) ALOGD("Removing alarms with time <= %u", timestampSec); + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> oldAlarms; + std::lock_guard<std::mutex> lock(mLock); + + for (sp<const AnomalyAlarm> t = mPq.top(); + t != nullptr && t->timestampSec <= timestampSec; t = mPq.top()) { + oldAlarms.insert(t); + mPq.pop(); // remove t + } + // Always update registered alarm time (if anything has changed). + if (!oldAlarms.empty()) { + if (mPq.empty()) { + if (DEBUG) ALOGD("Queue is empty. Cancel any alarm."); + mRegisteredAlarmTimeSec = 0; + if (mStatsCompanionService != nullptr) { + mStatsCompanionService->cancelAnomalyAlarm(); + } + } else { + // Always update the registered alarm in this case (unlike remove()). + updateRegisteredAlarmTime_l(mPq.top()->timestampSec); + } + } + return oldAlarms; +} + void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) { if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec); mRegisteredAlarmTimeSec = timestampSec; diff --git a/cmds/statsd/src/AnomalyMonitor.h b/cmds/statsd/src/AnomalyMonitor.h index d78be5460572..7c6e5e8945a7 100644 --- a/cmds/statsd/src/AnomalyMonitor.h +++ b/cmds/statsd/src/AnomalyMonitor.h @@ -21,12 +21,14 @@ #include <indexed_priority_queue.h> #include <utils/RefBase.h> +#include <unordered_set> #include <queue> #include <vector> using namespace android; using android::os::IStatsCompanionService; +using std::unordered_set; namespace android { namespace os { @@ -86,6 +88,13 @@ public: void remove(sp<const AnomalyAlarm> alarm); /** + * Returns and removes all alarms whose timestamp <= the given timestampSec. + * Always updates the registered alarm if return is non-empty. + */ + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> + popSoonerThan(uint32_t timestampSec); + + /** * Returns the projected alarm timestamp that is registered with * StatsCompanionService. This may not be equal to the soonest alarm, * but should be within minDiffToUpdateRegisteredAlarmTimeSec of it. diff --git a/cmds/statsd/src/KernelWakelockPuller.cpp b/cmds/statsd/src/KernelWakelockPuller.cpp new file mode 100644 index 000000000000..1798f9dec5ef --- /dev/null +++ b/cmds/statsd/src/KernelWakelockPuller.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "KernelWakelockPuller.h" +#include <android/os/IStatsCompanionService.h> +#include <binder/IPCThreadState.h> +#include <cutils/log.h> +#include <private/android_filesystem_config.h> +#include "StatsPuller.h" +#include "StatsService.h" + +using namespace android; +using namespace android::base; +using namespace android::binder; +using namespace android::os; +using namespace std; + +namespace android { +namespace os { +namespace statsd { + +const int KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS = 20; + +// The reading and parsing are implemented in Java. It is not difficult to port over. But for now +// let StatsCompanionService handle that and send the data back. +String16 KernelWakelockPuller::pull() { + sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService(); + String16 returned_value(""); + if (statsCompanion != NULL) { + Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS, + &returned_value); + if (!status.isOk()) { + ALOGW("error pulling kernel wakelock"); + } + ALOGD("KernelWakelockPuller::pull succeeded!"); + // TODO: remove this when we integrate into aggregation chain. + ALOGD("%s", String8(returned_value).string()); + return returned_value; + } else { + ALOGW("statsCompanion not found!"); + return String16(); + } +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/KernelWakelockPuller.h b/cmds/statsd/src/KernelWakelockPuller.h new file mode 100644 index 000000000000..1c16f8703082 --- /dev/null +++ b/cmds/statsd/src/KernelWakelockPuller.h @@ -0,0 +1,39 @@ +/* + * 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 STATSD_KERNELWAKELOCKPULLER_H +#define STATSD_KERNELWAKELOCKPULLER_H + +#include <utils/String16.h> +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +class KernelWakelockPuller : public StatsPuller { +public: + // a number of stats need to be pulled from StatsCompanionService + // + const static int PULL_CODE_KERNEL_WAKELOCKS; + String16 pull() override; +}; + +} // namespace statsd +} // namespace os +} // namespace android + +#endif // STATSD_KERNELWAKELOCKPULLER_H 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/StatsPuller.h b/cmds/statsd/src/StatsPuller.h index 05343b541972..5e556b89b521 100644 --- a/cmds/statsd/src/StatsPuller.h +++ b/cmds/statsd/src/StatsPuller.h @@ -25,19 +25,13 @@ namespace statsd { class StatsPuller { public: - // Enums of pulled data types (pullCodes) - // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java. - // TODO: pull the constant from stats_events.proto instead - const static int PULL_CODE_KERNEL_WAKELOCKS = 20; - - StatsPuller(); - ~StatsPuller(); - - static String16 pull(int pullCode); + virtual ~StatsPuller(){}; + // use string for now, until we figure out how to integrate into the aggregation path + virtual String16 pull() = 0; }; } // namespace statsd } // namespace os } // namespace android -#endif //STATSD_STATSPULLER_H +#endif // STATSD_STATSPULLER_H diff --git a/cmds/statsd/src/StatsPuller.cpp b/cmds/statsd/src/StatsPullerManager.cpp index 94e8361b6425..f4cf1ceaf18a 100644 --- a/cmds/statsd/src/StatsPuller.cpp +++ b/cmds/statsd/src/StatsPullerManager.cpp @@ -14,13 +14,15 @@ * limitations under the License. */ -#define LOG_TAG "StatsPuller" +#define LOG_TAG "StatsPullerManager" #define DEBUG true -#include "StatsPuller.h" -#include "StatsService.h" +#include "StatsPullerManager.h" #include <android/os/IStatsCompanionService.h> #include <cutils/log.h> +#include "StatsService.h" +#include "KernelWakelockPuller.h" + using namespace android; @@ -28,30 +30,20 @@ namespace android { namespace os { namespace statsd { -String16 StatsPuller::pull(int pullCode) { - if (DEBUG) ALOGD("Initiating pulling %d", pullCode); +const int StatsPullerManager::KERNEL_WAKELOCKS = 1; - switch (pullCode) { - // All stats_companion_service cases go here with fallthroughs - case PULL_CODE_KERNEL_WAKELOCKS: { - // TODO: Consider caching the statsCompanion service - sp <IStatsCompanionService> - statsCompanion = StatsService::getStatsCompanionService(); - String16 returned_value(""); - Status status = statsCompanion->pullData(pullCode, &returned_value); - if (DEBUG) ALOGD("Finished pulling the data"); - if (!status.isOk()) { - ALOGW("error pulling data of type %d", pullCode); - } - return returned_value; - } - - // case OTHER_TYPES: etc. - - default: { - ALOGE("invalid pull code %d", pullCode); - return String16(""); - } +StatsPullerManager::StatsPullerManager() { + mStatsPullers.insert( + {static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()}); +} + +String16 StatsPullerManager::pull(int pullCode) { + if (DEBUG) ALOGD("Initiating pulling %d", pullCode); + if (mStatsPullers.find(pullCode) != mStatsPullers.end()) { + return (mStatsPullers.find(pullCode)->second)->pull(); + } else { + ALOGD("Unknown pull code %d", pullCode); + return String16(); } } diff --git a/cmds/statsd/src/StatsPullerManager.h b/cmds/statsd/src/StatsPullerManager.h new file mode 100644 index 000000000000..ab36df535ae5 --- /dev/null +++ b/cmds/statsd/src/StatsPullerManager.h @@ -0,0 +1,49 @@ +/* + * 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 STATSD_STATSPULLERMANAGER_H +#define STATSD_STATSPULLERMANAGER_H + +#include <utils/String16.h> +#include <unordered_map> +#include "StatsPuller.h" + +namespace android { +namespace os { +namespace statsd { + +const static int KERNEL_WAKELOCKS = 1; + +class StatsPullerManager { +public: + // Enums of pulled data types (pullCodes) + // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java. + // TODO: pull the constant from stats_events.proto instead + const static int KERNEL_WAKELOCKS; + StatsPullerManager(); + + String16 pull(const int pullCode); + +private: + std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers; +}; + + +} // namespace statsd +} // namespace os +} // namespace android + +#endif // STATSD_STATSPULLERMANAGER_H diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index ae7d66bf0da9..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)) // 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"); @@ -177,9 +218,10 @@ Status StatsService::informPollAlarmFired() { if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded"); // TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them. - String16 output = StatsPuller::pull(StatsPuller::PULL_CODE_KERNEL_WAKELOCKS); + String16 output = mStatsPullerManager.pull(StatsPullerManager::KERNEL_WAKELOCKS); // TODO: do something useful with the output instead of writing a string to screen. ALOGD("%s", String8(output).string()); + ALOGD("%d", int(output.size())); return Status::ok(); } @@ -260,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 a16b115baf8e..541f7e8be7fa 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -19,7 +19,9 @@ #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: +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); @@ -82,6 +95,10 @@ public: void printCmdHelp(FILE* out); 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/indexed_priority_queue.h b/cmds/statsd/src/indexed_priority_queue.h index c749c3ee6d17..81e8b3d023ea 100644 --- a/cmds/statsd/src/indexed_priority_queue.h +++ b/cmds/statsd/src/indexed_priority_queue.h @@ -55,6 +55,8 @@ public: void push(sp<const AA> a); /** Removes a from the priority queue. If not present or a==nullptr, does nothing. */ void remove(sp<const AA> a); + /** Removes the top element, if there is one. */ + void pop(); /** Removes all elements. */ void clear(); /** Returns whether priority queue contains a (not just a copy of a, but a itself). */ @@ -127,6 +129,28 @@ void indexed_priority_queue<AA, Comparator>::remove(sp<const AA> a) { sift_down(idx); } +// The same as, but slightly more efficient than, remove(top()). +template <class AA, class Comparator> +void indexed_priority_queue<AA, Comparator>::pop() { + sp<const AA> a = top(); + if (a == nullptr) return; + const size_t idx = 1; + if (idx == size()) { // if a is the last element + pq.pop_back(); + indices.erase(a); + return; + } + // move last element (guaranteed not to be at idx) to idx, then delete a + sp<const AA> last_a = pq.back(); + pq[idx] = last_a; + pq.pop_back(); + indices[last_a] = idx; + indices.erase(a); + + // get the heap back in order (since the element at idx is not in order) + sift_down(idx); +} + template <class AA, class Comparator> void indexed_priority_queue<AA, Comparator>::clear() { pq.clear(); 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/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AnomalyMonitor_test.cpp new file mode 100644 index 000000000000..d5b68118d119 --- /dev/null +++ b/cmds/statsd/tests/AnomalyMonitor_test.cpp @@ -0,0 +1,66 @@ +// 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 "../src/AnomalyMonitor.h" + +#include <gtest/gtest.h> + +using namespace android::os::statsd; + +#ifdef __ANDROID__ +TEST(AnomalyMonitor, popSoonerThan) { + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> set; + AnomalyMonitor am(2); + + set = am.popSoonerThan(5); + EXPECT_TRUE(set.empty()); + + sp<const AnomalyAlarm> a = new AnomalyAlarm{10}; + sp<const AnomalyAlarm> b = new AnomalyAlarm{20}; + sp<const AnomalyAlarm> c = new AnomalyAlarm{20}; + sp<const AnomalyAlarm> d = new AnomalyAlarm{30}; + sp<const AnomalyAlarm> e = new AnomalyAlarm{40}; + sp<const AnomalyAlarm> f = new AnomalyAlarm{50}; + + am.add(a); + am.add(b); + am.add(c); + am.add(d); + am.add(e); + am.add(f); + + set = am.popSoonerThan(5); + EXPECT_TRUE(set.empty()); + + set = am.popSoonerThan(30); + EXPECT_EQ(4u, set.size()); + EXPECT_EQ(1u, set.count(a)); + EXPECT_EQ(1u, set.count(b)); + EXPECT_EQ(1u, set.count(c)); + EXPECT_EQ(1u, set.count(d)); + + set = am.popSoonerThan(60); + EXPECT_EQ(2u, set.size()); + EXPECT_EQ(1u, set.count(e)); + EXPECT_EQ(1u, set.count(f)); + + set = am.popSoonerThan(80); + EXPECT_EQ(0u, set.size()); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif 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/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp index e4d4d25afe00..74a482eace58 100644 --- a/cmds/statsd/tests/indexed_priority_queue_test.cpp +++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp @@ -182,6 +182,40 @@ TEST(indexed_priority_queue, nulls) { EXPECT_FALSE(ipq.contains(nullptr)); } +TEST(indexed_priority_queue, pop) { + indexed_priority_queue<AATest, AATest::Smaller> ipq; + sp<const AATest> a = new AATest{1}; + sp<const AATest> b = new AATest{2}; + sp<const AATest> c = new AATest{3}; + + ipq.push(c); + ipq.push(b); + ipq.push(a); + EXPECT_EQ(3u, ipq.size()); + + ipq.pop(); + EXPECT_EQ(2u, ipq.size()); + EXPECT_FALSE(ipq.contains(a)); + EXPECT_TRUE(ipq.contains(b)); + EXPECT_TRUE(ipq.contains(c)); + + ipq.pop(); + EXPECT_EQ(1u, ipq.size()); + EXPECT_FALSE(ipq.contains(a)); + EXPECT_FALSE(ipq.contains(b)); + EXPECT_TRUE(ipq.contains(c)); + + ipq.pop(); + EXPECT_EQ(0u, ipq.size()); + EXPECT_FALSE(ipq.contains(a)); + EXPECT_FALSE(ipq.contains(b)); + EXPECT_FALSE(ipq.contains(c)); + EXPECT_TRUE(ipq.empty()); + + ipq.pop(); // pop an empty queue + EXPECT_TRUE(ipq.empty()); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index e0ac91130385..d988a422354e 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -542,9 +542,9 @@ import java.util.List; * <ul> * <li> <p>When creating a new document, the backing database entry or file for * it is created immediately. For example, if the user chooses to write - * a new e-mail, a new entry for that e-mail is created as soon as they + * a new email, a new entry for that email is created as soon as they * start entering data, so that if they go to any other activity after - * that point this e-mail will now appear in the list of drafts.</p> + * that point this email will now appear in the list of drafts.</p> * <li> <p>When an activity's <code>onPause()</code> method is called, it should * commit to the backing content provider or file any changes the user * has made. This ensures that those changes will be seen by any other 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/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index be7f921ea1fd..143c51da5367 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -467,6 +467,7 @@ public abstract class PackageManagerInternal { /** Updates the flags for the given permission. */ public abstract void updatePermissionFlagsTEMP(@NonNull String permName, @NonNull String packageName, int flagMask, int flagValues, int userId); - /** temporary until mPermissionTrees is moved to PermissionManager */ - public abstract Object enforcePermissionTreeTEMP(@NonNull String permName, int callingUid); + /** Returns a PermissionGroup. */ + public abstract @Nullable PackageParser.PermissionGroup getPermissionGroupTEMP( + @NonNull String groupName); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 6c7c8a077905..ec48ac5e40dc 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -6849,6 +6849,11 @@ public class PackageParser { dest.writeParcelable(group, flags); } + /** @hide */ + public boolean isAppOp() { + return info.isAppOp(); + } + private Permission(Parcel in) { super(in); final ClassLoader boot = Object.class.getClassLoader(); diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index b45c26ce73ee..5dd7aeda408f 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -353,6 +353,11 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { return size; } + /** @hide */ + public boolean isAppOp() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0; + } + public static final Creator<PermissionInfo> CREATOR = new Creator<PermissionInfo>() { @Override 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/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 2c9fb23e077a..4e474c8e478c 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -683,9 +683,9 @@ public final class LinkProperties implements Parcelable { */ public boolean hasIPv4Address() { for (LinkAddress address : mLinkAddresses) { - if (address.getAddress() instanceof Inet4Address) { - return true; - } + if (address.getAddress() instanceof Inet4Address) { + return true; + } } return false; } @@ -725,9 +725,9 @@ public final class LinkProperties implements Parcelable { */ public boolean hasIPv4DefaultRoute() { for (RouteInfo r : mRoutes) { - if (r.isIPv4Default()) { - return true; - } + if (r.isIPv4Default()) { + return true; + } } return false; } @@ -740,9 +740,9 @@ public final class LinkProperties implements Parcelable { */ public boolean hasIPv6DefaultRoute() { for (RouteInfo r : mRoutes) { - if (r.isIPv6Default()) { - return true; - } + if (r.isIPv6Default()) { + return true; + } } return false; } @@ -755,9 +755,9 @@ public final class LinkProperties implements Parcelable { */ public boolean hasIPv4DnsServer() { for (InetAddress ia : mDnses) { - if (ia instanceof Inet4Address) { - return true; - } + if (ia instanceof Inet4Address) { + return true; + } } return false; } @@ -770,9 +770,9 @@ public final class LinkProperties implements Parcelable { */ public boolean hasIPv6DnsServer() { for (InetAddress ia : mDnses) { - if (ia instanceof Inet6Address) { - return true; - } + if (ia instanceof Inet6Address) { + return true; + } } return false; } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 988192797f59..450ced4b9897 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1911,6 +1911,13 @@ public abstract class BatteryStats implements Parcelable { long elapsedRealtimeUs, int which); /** + * Returns the {@link Timer} object that tracks the given screen brightness. + * + * {@hide} + */ + public abstract Timer getScreenBrightnessTimer(int brightnessBin); + + /** * Returns the time in microseconds that power save mode has been enabled while the device was * running on battery. * @@ -2019,6 +2026,14 @@ public abstract class BatteryStats implements Parcelable { long elapsedRealtimeUs, int which); /** + * Returns the {@link Timer} object that tracks how much the phone has been trying to + * acquire a signal. + * + * {@hide} + */ + public abstract Timer getPhoneSignalScanningTimer(); + + /** * Returns the number of times the phone has entered the given signal strength. * * {@hide} @@ -2026,6 +2041,12 @@ public abstract class BatteryStats implements Parcelable { public abstract int getPhoneSignalStrengthCount(int strengthBin, int which); /** + * Return the {@link Timer} object used to track the given signal strength's duration and + * counts. + */ + protected abstract Timer getPhoneSignalStrengthTimer(int strengthBin); + + /** * Returns the time in microseconds that the mobile network has been active * (in a high power state). * @@ -2108,6 +2129,11 @@ public abstract class BatteryStats implements Parcelable { */ public abstract int getPhoneDataConnectionCount(int dataType, int which); + /** + * Returns the {@link Timer} object that tracks the phone's data connection type stats. + */ + public abstract Timer getPhoneDataConnectionTimer(int dataType); + public static final int WIFI_SUPPL_STATE_INVALID = 0; public static final int WIFI_SUPPL_STATE_DISCONNECTED = 1; public static final int WIFI_SUPPL_STATE_INTERFACE_DISABLED = 2; @@ -2267,6 +2293,13 @@ public abstract class BatteryStats implements Parcelable { public abstract int getWifiStateCount(int wifiState, int which); /** + * Returns the {@link Timer} object that tracks the given WiFi state. + * + * {@hide} + */ + public abstract Timer getWifiStateTimer(int wifiState); + + /** * Returns the time in microseconds that the wifi supplicant has been * in a given state. * @@ -2282,6 +2315,13 @@ public abstract class BatteryStats implements Parcelable { */ public abstract int getWifiSupplStateCount(int state, int which); + /** + * Returns the {@link Timer} object that tracks the given wifi supplicant state. + * + * {@hide} + */ + public abstract Timer getWifiSupplStateTimer(int state); + public static final int NUM_WIFI_SIGNAL_STRENGTH_BINS = 5; /** @@ -2301,6 +2341,13 @@ public abstract class BatteryStats implements Parcelable { public abstract int getWifiSignalStrengthCount(int strengthBin, int which); /** + * Returns the {@link Timer} object that tracks the given WIFI signal strength. + * + * {@hide} + */ + public abstract Timer getWifiSignalStrengthTimer(int strengthBin); + + /** * Returns the time in microseconds that the flashlight has been on while the device was * running on battery. * @@ -2487,13 +2534,13 @@ public abstract class BatteryStats implements Parcelable { public abstract int getDischargeAmountScreenOffSinceCharge(); /** - * Get the amount the battery has discharged while the screen was doze, + * Get the amount the battery has discharged while the screen was dozing, * since the last time power was unplugged. */ public abstract int getDischargeAmountScreenDoze(); /** - * Get the amount the battery has discharged while the screen was doze, + * Get the amount the battery has discharged while the screen was dozing, * since the last time the device was charged. */ public abstract int getDischargeAmountScreenDozeSinceCharge(); @@ -2626,20 +2673,20 @@ public abstract class BatteryStats implements Parcelable { * micro-Ampere-hours. This will be non-zero only if the device's battery has * a coulomb counter. */ - public abstract long getMahDischargeScreenOff(int which); + public abstract long getUahDischargeScreenOff(int which); /** * Return the amount of battery discharge while the screen was in doze mode, measured in * micro-Ampere-hours. This will be non-zero only if the device's battery has * a coulomb counter. */ - public abstract long getMahDischargeScreenDoze(int which); + public abstract long getUahDischargeScreenDoze(int which); /** * Return the amount of battery discharge measured in micro-Ampere-hours. This will be * non-zero only if the device's battery has a coulomb counter. */ - public abstract long getMahDischarge(int which); + public abstract long getUahDischarge(int which); /** * Returns the estimated real battery capacity, which may be less than the capacity @@ -2984,7 +3031,7 @@ public abstract class BatteryStats implements Parcelable { final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; final int count = timer.getCountLocked(which); - if (totalTime != 0) { + if (totalTime != 0 || count != 0) { dumpLine(pw, uid, category, type, totalTime, count); } } @@ -3000,12 +3047,12 @@ public abstract class BatteryStats implements Parcelable { * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT */ private static void dumpTimer(ProtoOutputStream proto, long fieldId, - Timer timer, long rawRealtime, int which) { + Timer timer, long rawRealtimeUs, int which) { if (timer == null) { return; } // Convert from microseconds to milliseconds with rounding - final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; + final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtimeUs, which) + 500) / 1000; final int count = timer.getCountLocked(which); if (totalTimeMs != 0 || count != 0) { final long token = proto.start(fieldId); @@ -3191,13 +3238,13 @@ public abstract class BatteryStats implements Parcelable { /** * Checkin server version of dump to produce more compact, computer-readable log. * - * NOTE: all times are expressed in 'ms'. + * NOTE: all times are expressed in microseconds, unless specified otherwise. */ public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid, boolean wifiOnly) { final long rawUptime = SystemClock.uptimeMillis() * 1000; - final long rawRealtime = SystemClock.elapsedRealtime() * 1000; - final long rawRealtimeMs = (rawRealtime + 500) / 1000; + final long rawRealtimeMs = SystemClock.elapsedRealtime(); + final long rawRealtime = rawRealtimeMs * 1000; final long batteryUptime = getBatteryUptime(rawUptime); final long whichBatteryUptime = computeBatteryUptime(rawUptime, which); final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which); @@ -3220,9 +3267,9 @@ public abstract class BatteryStats implements Parcelable { rawRealtime, which); final int connChanges = getNumConnectivityChange(which); final long phoneOnTime = getPhoneOnTime(rawRealtime, which); - final long dischargeCount = getMahDischarge(which); - final long dischargeScreenOffCount = getMahDischargeScreenOff(which); - final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which); + final long dischargeCount = getUahDischarge(which); + final long dischargeScreenOffCount = getUahDischargeScreenOff(which); + final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which); final StringBuilder sb = new StringBuilder(128); @@ -3460,9 +3507,9 @@ public abstract class BatteryStats implements Parcelable { BatteryStatsHelper.makemAh(helper.getComputedPower()), BatteryStatsHelper.makemAh(helper.getMinDrainedPower()), BatteryStatsHelper.makemAh(helper.getMaxDrainedPower())); + int uid = 0; for (int i=0; i<sippers.size(); i++) { final BatterySipper bs = sippers.get(i); - int uid = 0; String label; switch (bs.drainType) { case IDLE: @@ -3503,6 +3550,9 @@ public abstract class BatteryStats implements Parcelable { case CAMERA: label = "camera"; break; + case MEMORY: + label = "memory"; + break; default: label = "???"; } @@ -3523,6 +3573,7 @@ public abstract class BatteryStats implements Parcelable { dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString()); } + // Dump stats per UID. for (int iu = 0; iu < NU; iu++) { final int uid = uidStats.keyAt(iu); if (reqUid >= 0 && uid != reqUid) { @@ -4020,7 +4071,7 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } - final long dischargeCount = getMahDischarge(which); + final long dischargeCount = getUahDischarge(which); if (dischargeCount >= 0) { sb.setLength(0); sb.append(prefix); @@ -4030,7 +4081,7 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } - final long dischargeScreenOffCount = getMahDischargeScreenOff(which); + final long dischargeScreenOffCount = getUahDischargeScreenOff(which); if (dischargeScreenOffCount >= 0) { sb.setLength(0); sb.append(prefix); @@ -4040,7 +4091,7 @@ public abstract class BatteryStats implements Parcelable { pw.println(sb.toString()); } - final long dischargeScreenDozeCount = getMahDischargeScreenDoze(which); + final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which); if (dischargeScreenDozeCount >= 0) { sb.setLength(0); sb.append(prefix); @@ -6038,6 +6089,61 @@ public abstract class BatteryStats implements Parcelable { return true; } + private static void dumpDurationSteps(ProtoOutputStream proto, long fieldId, + LevelStepTracker steps) { + if (steps == null) { + return; + } + int count = steps.mNumStepDurations; + long token; + for (int i = 0; i < count; ++i) { + token = proto.start(fieldId); + proto.write(SystemProto.BatteryLevelStep.DURATION_MS, steps.getDurationAt(i)); + proto.write(SystemProto.BatteryLevelStep.LEVEL, steps.getLevelAt(i)); + + final long initMode = steps.getInitModeAt(i); + final long modMode = steps.getModModeAt(i); + + int ds = SystemProto.BatteryLevelStep.DS_MIXED; + if ((modMode & STEP_LEVEL_MODE_SCREEN_STATE) == 0) { + switch ((int) (initMode & STEP_LEVEL_MODE_SCREEN_STATE) + 1) { + case Display.STATE_OFF: + ds = SystemProto.BatteryLevelStep.DS_OFF; + break; + case Display.STATE_ON: + ds = SystemProto.BatteryLevelStep.DS_ON; + break; + case Display.STATE_DOZE: + ds = SystemProto.BatteryLevelStep.DS_DOZE; + break; + case Display.STATE_DOZE_SUSPEND: + ds = SystemProto.BatteryLevelStep.DS_DOZE_SUSPEND; + break; + default: + ds = SystemProto.BatteryLevelStep.DS_ERROR; + break; + } + } + proto.write(SystemProto.BatteryLevelStep.DISPLAY_STATE, ds); + + int psm = SystemProto.BatteryLevelStep.PSM_MIXED; + if ((modMode & STEP_LEVEL_MODE_POWER_SAVE) == 0) { + psm = (initMode & STEP_LEVEL_MODE_POWER_SAVE) != 0 + ? SystemProto.BatteryLevelStep.PSM_ON : SystemProto.BatteryLevelStep.PSM_OFF; + } + proto.write(SystemProto.BatteryLevelStep.POWER_SAVE_MODE, psm); + + int im = SystemProto.BatteryLevelStep.IM_MIXED; + if ((modMode & STEP_LEVEL_MODE_DEVICE_IDLE) == 0) { + im = (initMode & STEP_LEVEL_MODE_DEVICE_IDLE) != 0 + ? SystemProto.BatteryLevelStep.IM_ON : SystemProto.BatteryLevelStep.IM_OFF; + } + proto.write(SystemProto.BatteryLevelStep.IDLE_MODE, im); + + proto.end(token); + } + } + public static final int DUMP_CHARGED_ONLY = 1<<1; public static final int DUMP_DAILY_ONLY = 1<<2; public static final int DUMP_HISTORY_ONLY = 1<<3; @@ -6463,7 +6569,7 @@ public abstract class BatteryStats implements Parcelable { } } - /** Dump batterystats data to a proto. @hide */ + /** Dump #STATS_SINCE_CHARGED batterystats data to a proto. @hide */ public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps, int flags, long historyStart) { final ProtoOutputStream proto = new ProtoOutputStream(fd); @@ -6485,10 +6591,376 @@ public abstract class BatteryStats implements Parcelable { if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) { // TODO: implement dumpProtoAppsLocked(proto, apps); - // TODO: implement dumpProtoSystemLocked(proto); + dumpProtoSystemLocked(context, proto, (flags & DUMP_DEVICE_WIFI_ONLY) != 0); } proto.end(bToken); proto.flush(); } + + private void dumpProtoSystemLocked(Context context, ProtoOutputStream proto, boolean wifiOnly) { + final long sToken = proto.start(BatteryStatsProto.SYSTEM); + final long rawUptimeUs = SystemClock.uptimeMillis() * 1000; + final long rawRealtimeMs = SystemClock.elapsedRealtime(); + final long rawRealtimeUs = rawRealtimeMs * 1000; + final int which = STATS_SINCE_CHARGED; + + // Battery data (BATTERY_DATA) + long token = proto.start(SystemProto.BATTERY); + proto.write(SystemProto.Battery.START_CLOCK_TIME_MS, getStartClockTime()); + proto.write(SystemProto.Battery.START_COUNT, getStartCount()); + proto.write(SystemProto.Battery.TOTAL_REALTIME_MS, + computeRealtime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Battery.TOTAL_UPTIME_MS, + computeUptime(rawUptimeUs, which) / 1000); + proto.write(SystemProto.Battery.BATTERY_REALTIME_MS, + computeBatteryRealtime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Battery.BATTERY_UPTIME_MS, + computeBatteryUptime(rawUptimeUs, which) / 1000); + proto.write(SystemProto.Battery.SCREEN_OFF_REALTIME_MS, + computeBatteryScreenOffRealtime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Battery.SCREEN_OFF_UPTIME_MS, + computeBatteryScreenOffUptime(rawUptimeUs, which) / 1000); + proto.write(SystemProto.Battery.SCREEN_DOZE_DURATION_MS, + getScreenDozeTime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Battery.ESTIMATED_BATTERY_CAPACITY_MAH, + getEstimatedBatteryCapacity()); + proto.write(SystemProto.Battery.MIN_LEARNED_BATTERY_CAPACITY_UAH, + getMinLearnedBatteryCapacity()); + proto.write(SystemProto.Battery.MAX_LEARNED_BATTERY_CAPACITY_UAH, + getMaxLearnedBatteryCapacity()); + proto.end(token); + + // Battery discharge (BATTERY_DISCHARGE_DATA) + token = proto.start(SystemProto.BATTERY_DISCHARGE); + proto.write(SystemProto.BatteryDischarge.LOWER_BOUND_SINCE_CHARGE, + getLowDischargeAmountSinceCharge()); + proto.write(SystemProto.BatteryDischarge.UPPER_BOUND_SINCE_CHARGE, + getHighDischargeAmountSinceCharge()); + proto.write(SystemProto.BatteryDischarge.SCREEN_ON_SINCE_CHARGE, + getDischargeAmountScreenOnSinceCharge()); + proto.write(SystemProto.BatteryDischarge.SCREEN_OFF_SINCE_CHARGE, + getDischargeAmountScreenOffSinceCharge()); + proto.write(SystemProto.BatteryDischarge.SCREEN_DOZE_SINCE_CHARGE, + getDischargeAmountScreenDozeSinceCharge()); + proto.write(SystemProto.BatteryDischarge.TOTAL_MAH, + getUahDischarge(which) / 1000); + proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_OFF, + getUahDischargeScreenOff(which) / 1000); + proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_DOZE, + getUahDischargeScreenDoze(which) / 1000); + proto.end(token); + + // Time remaining + long timeRemainingUs = computeChargeTimeRemaining(rawRealtimeUs); + if (timeRemainingUs >= 0) { + // Charge time remaining (CHARGE_TIME_REMAIN_DATA) + proto.write(SystemProto.CHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000); + } else { + timeRemainingUs = computeBatteryTimeRemaining(rawRealtimeUs); + // Discharge time remaining (DISCHARGE_TIME_REMAIN_DATA) + if (timeRemainingUs >= 0) { + proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000); + } else { + proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, -1); + } + } + + // Charge step (CHARGE_STEP_DATA) + dumpDurationSteps(proto, SystemProto.CHARGE_STEP, getChargeLevelStepTracker()); + + // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA) + for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) { + token = proto.start(SystemProto.DATA_CONNECTION); + proto.write(SystemProto.DataConnection.NAME, i); + dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i), + rawRealtimeUs, which); + proto.end(token); + } + + // Discharge step (DISCHARGE_STEP_DATA) + dumpDurationSteps(proto, SystemProto.DISCHARGE_STEP, getDischargeLevelStepTracker()); + + // CPU frequencies (GLOBAL_CPU_FREQ_DATA) + final long[] cpuFreqs = getCpuFreqs(); + if (cpuFreqs != null) { + for (long i : cpuFreqs) { + proto.write(SystemProto.CPU_FREQUENCY, i); + } + } + + // Bluetooth controller (GLOBAL_BLUETOOTH_CONTROLLER_DATA) + dumpControllerActivityProto(proto, SystemProto.GLOBAL_BLUETOOTH_CONTROLLER, + getBluetoothControllerActivity(), which); + + // Modem controller (GLOBAL_MODEM_CONTROLLER_DATA) + dumpControllerActivityProto(proto, SystemProto.GLOBAL_MODEM_CONTROLLER, + getModemControllerActivity(), which); + + // Global network data (GLOBAL_NETWORK_DATA) + token = proto.start(SystemProto.GLOBAL_NETWORK); + proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_RX, + getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_TX, + getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_RX, + getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_TX, + getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_RX, + getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_TX, + getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_RX, + getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_TX, + getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.BT_BYTES_RX, + getNetworkActivityBytes(NETWORK_BT_RX_DATA, which)); + proto.write(SystemProto.GlobalNetwork.BT_BYTES_TX, + getNetworkActivityBytes(NETWORK_BT_TX_DATA, which)); + proto.end(token); + + // Wifi controller (GLOBAL_WIFI_CONTROLLER_DATA) + dumpControllerActivityProto(proto, SystemProto.GLOBAL_WIFI_CONTROLLER, + getWifiControllerActivity(), which); + + + // Global wifi (GLOBAL_WIFI_DATA) + token = proto.start(SystemProto.GLOBAL_WIFI); + proto.write(SystemProto.GlobalWifi.ON_DURATION_MS, + getWifiOnTime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.GlobalWifi.RUNNING_DURATION_MS, + getGlobalWifiRunningTime(rawRealtimeUs, which) / 1000); + proto.end(token); + + // Kernel wakelock (KERNEL_WAKELOCK_DATA) + final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats(); + for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) { + token = proto.start(SystemProto.KERNEL_WAKELOCK); + proto.write(SystemProto.KernelWakelock.NAME, ent.getKey()); + dumpTimer(proto, SystemProto.KernelWakelock.TOTAL, ent.getValue(), + rawRealtimeUs, which); + proto.end(token); + } + + // Misc (MISC_DATA) + // Calculate wakelock times across all uids. + long fullWakeLockTimeTotalUs = 0; + long partialWakeLockTimeTotalUs = 0; + + final SparseArray<? extends Uid> uidStats = getUidStats(); + for (int iu = 0; iu < uidStats.size(); iu++) { + final Uid u = uidStats.valueAt(iu); + + final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = + u.getWakelockStats(); + for (int iw = wakelocks.size() - 1; iw >= 0; --iw) { + final Uid.Wakelock wl = wakelocks.valueAt(iw); + + final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL); + if (fullWakeTimer != null) { + fullWakeLockTimeTotalUs += fullWakeTimer.getTotalTimeLocked(rawRealtimeUs, + which); + } + + final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL); + if (partialWakeTimer != null) { + partialWakeLockTimeTotalUs += partialWakeTimer.getTotalTimeLocked( + rawRealtimeUs, which); + } + } + } + token = proto.start(SystemProto.MISC); + proto.write(SystemProto.Misc.SCREEN_ON_DURATION_MS, + getScreenOnTime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.PHONE_ON_DURATION_MS, + getPhoneOnTime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.FULL_WAKELOCK_TOTAL_DURATION_MS, + fullWakeLockTimeTotalUs / 1000); + proto.write(SystemProto.Misc.PARTIAL_WAKELOCK_TOTAL_DURATION_MS, + partialWakeLockTimeTotalUs / 1000); + proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_DURATION_MS, + getMobileRadioActiveTime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_ADJUSTED_TIME_MS, + getMobileRadioActiveAdjustedTime(which) / 1000); + proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_COUNT, + getMobileRadioActiveCount(which)); + proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_UNKNOWN_DURATION_MS, + getMobileRadioActiveUnknownTime(which) / 1000); + proto.write(SystemProto.Misc.INTERACTIVE_DURATION_MS, + getInteractiveTime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.BATTERY_SAVER_MODE_ENABLED_DURATION_MS, + getPowerSaveModeEnabledTime(rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.NUM_CONNECTIVITY_CHANGES, + getNumConnectivityChange(which)); + proto.write(SystemProto.Misc.DEEP_DOZE_ENABLED_DURATION_MS, + getDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.DEEP_DOZE_COUNT, + getDeviceIdleModeCount(DEVICE_IDLE_MODE_DEEP, which)); + proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_DURATION_MS, + getDeviceIdlingTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_COUNT, + getDeviceIdlingCount(DEVICE_IDLE_MODE_DEEP, which)); + proto.write(SystemProto.Misc.LONGEST_DEEP_DOZE_DURATION_MS, + getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP)); + proto.write(SystemProto.Misc.LIGHT_DOZE_ENABLED_DURATION_MS, + getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.LIGHT_DOZE_COUNT, + getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which)); + proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_DURATION_MS, + getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000); + proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_COUNT, + getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which)); + proto.write(SystemProto.Misc.LONGEST_LIGHT_DOZE_DURATION_MS, + getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT)); + proto.end(token); + + final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly); + helper.create(this); + helper.refreshStats(which, UserHandle.USER_ALL); + + // Power use item (POWER_USE_ITEM_DATA) + final List<BatterySipper> sippers = helper.getUsageList(); + if (sippers != null) { + for (int i = 0; i < sippers.size(); ++i) { + final BatterySipper bs = sippers.get(i); + int n = SystemProto.PowerUseItem.UNKNOWN_SIPPER; + int uid = 0; + switch (bs.drainType) { + case IDLE: + n = SystemProto.PowerUseItem.IDLE; + break; + case CELL: + n = SystemProto.PowerUseItem.CELL; + break; + case PHONE: + n = SystemProto.PowerUseItem.PHONE; + break; + case WIFI: + n = SystemProto.PowerUseItem.WIFI; + break; + case BLUETOOTH: + n = SystemProto.PowerUseItem.BLUETOOTH; + break; + case SCREEN: + n = SystemProto.PowerUseItem.SCREEN; + break; + case FLASHLIGHT: + n = SystemProto.PowerUseItem.FLASHLIGHT; + break; + case APP: + // dumpProtoAppLocked will handle this. + continue; + case USER: + n = SystemProto.PowerUseItem.USER; + uid = UserHandle.getUid(bs.userId, 0); + break; + case UNACCOUNTED: + n = SystemProto.PowerUseItem.UNACCOUNTED; + break; + case OVERCOUNTED: + n = SystemProto.PowerUseItem.OVERCOUNTED; + break; + case CAMERA: + n = SystemProto.PowerUseItem.CAMERA; + break; + case MEMORY: + n = SystemProto.PowerUseItem.MEMORY; + break; + } + token = proto.start(SystemProto.POWER_USE_ITEM); + proto.write(SystemProto.PowerUseItem.NAME, n); + proto.write(SystemProto.PowerUseItem.UID, uid); + proto.write(SystemProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah); + proto.write(SystemProto.PowerUseItem.SHOULD_HIDE, bs.shouldHide); + proto.write(SystemProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah); + proto.write(SystemProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH, + bs.proportionalSmearMah); + proto.end(token); + } + } + + // Power use summary (POWER_USE_SUMMARY_DATA) + token = proto.start(SystemProto.POWER_USE_SUMMARY); + proto.write(SystemProto.PowerUseSummary.BATTERY_CAPACITY_MAH, + helper.getPowerProfile().getBatteryCapacity()); + proto.write(SystemProto.PowerUseSummary.COMPUTED_POWER_MAH, helper.getComputedPower()); + proto.write(SystemProto.PowerUseSummary.MIN_DRAINED_POWER_MAH, helper.getMinDrainedPower()); + proto.write(SystemProto.PowerUseSummary.MAX_DRAINED_POWER_MAH, helper.getMaxDrainedPower()); + proto.end(token); + + // RPM stats (RESOURCE_POWER_MANAGER_DATA) + final Map<String, ? extends Timer> rpmStats = getRpmStats(); + final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats(); + for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) { + token = proto.start(SystemProto.RESOURCE_POWER_MANAGER); + proto.write(SystemProto.ResourcePowerManager.NAME, ent.getKey()); + dumpTimer(proto, SystemProto.ResourcePowerManager.TOTAL, + ent.getValue(), rawRealtimeUs, which); + dumpTimer(proto, SystemProto.ResourcePowerManager.SCREEN_OFF, + screenOffRpmStats.get(ent.getKey()), rawRealtimeUs, which); + proto.end(token); + } + + // Screen brightness (SCREEN_BRIGHTNESS_DATA) + for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; ++i) { + token = proto.start(SystemProto.SCREEN_BRIGHTNESS); + proto.write(SystemProto.ScreenBrightness.NAME, i); + dumpTimer(proto, SystemProto.ScreenBrightness.TOTAL, getScreenBrightnessTimer(i), + rawRealtimeUs, which); + proto.end(token); + } + + // Signal scanning time (SIGNAL_SCANNING_TIME_DATA) + dumpTimer(proto, SystemProto.SIGNAL_SCANNING, getPhoneSignalScanningTimer(), rawRealtimeUs, + which); + + // Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA) + for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; ++i) { + token = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH); + proto.write(SystemProto.PhoneSignalStrength.NAME, i); + dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i), + rawRealtimeUs, which); + proto.end(token); + } + + // Wakeup reasons (WAKEUP_REASON_DATA) + final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats(); + for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) { + token = proto.start(SystemProto.WAKEUP_REASON); + proto.write(SystemProto.WakeupReason.NAME, ent.getKey()); + dumpTimer(proto, SystemProto.WakeupReason.TOTAL, ent.getValue(), rawRealtimeUs, which); + proto.end(token); + } + + // Wifi signal strength (WIFI_SIGNAL_STRENGTH_TIME_DATA and WIFI_SIGNAL_STRENGTH_COUNT_DATA) + for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; ++i) { + token = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH); + proto.write(SystemProto.WifiSignalStrength.NAME, i); + dumpTimer(proto, SystemProto.WifiSignalStrength.TOTAL, getWifiSignalStrengthTimer(i), + rawRealtimeUs, which); + proto.end(token); + } + + // Wifi state (WIFI_STATE_TIME_DATA and WIFI_STATE_COUNT_DATA) + for (int i = 0; i < NUM_WIFI_STATES; ++i) { + token = proto.start(SystemProto.WIFI_STATE); + proto.write(SystemProto.WifiState.NAME, i); + dumpTimer(proto, SystemProto.WifiState.TOTAL, getWifiStateTimer(i), + rawRealtimeUs, which); + proto.end(token); + } + + // Wifi supplicant state (WIFI_SUPPL_STATE_TIME_DATA and WIFI_SUPPL_STATE_COUNT_DATA) + for (int i = 0; i < NUM_WIFI_SUPPL_STATES; ++i) { + token = proto.start(SystemProto.WIFI_SUPPLICANT_STATE); + proto.write(SystemProto.WifiSupplicantState.NAME, i); + dumpTimer(proto, SystemProto.WifiSupplicantState.TOTAL, getWifiSupplStateTimer(i), + rawRealtimeUs, which); + proto.end(token); + } + + proto.end(sToken); + } } 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/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index c091420a31d4..7f588adbd69d 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -737,7 +737,9 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { private void closeWithStatus(int status, String msg) { if (mClosed) return; mClosed = true; - mGuard.close(); + if (mGuard != null) { + mGuard.close(); + } // Status MUST be sent before closing actual descriptor writeCommStatusAndClose(status, msg); IoUtils.closeQuietly(mFd); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 430a5e3e7281..8c688713c9aa 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -574,6 +574,25 @@ public class UserManager { public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows"; /** + * Specifies that system error dialogs for crashed or unresponsive apps should not be shown. + * In this case, the system will force-stop the app as if the user chooses the "close app" + * option on the UI. No feedback report will be collected as there is no way for the user to + * provide explicit consent. + * + * When this user restriction is set by device owners, it's applied to all users; when it's set + * by profile owners, it's only applied to the relevant profiles. + * The default value is <code>false</code>. + * + * <p>This user restriction has no effect on managed profiles. + * <p>Key for user restrictions. + * <p>Type: Boolean + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs"; + + /** * Specifies if what is copied in the clipboard of this profile can * be pasted in related profiles. Does not restrict if the clipboard of related profiles can be * pasted in this profile. 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..dd07ddb40ad4 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -681,17 +681,17 @@ public class BatteryStatsImpl extends BatteryStats { } @Override - public long getMahDischarge(int which) { + public long getUahDischarge(int which) { return mDischargeCounter.getCountLocked(which); } @Override - public long getMahDischargeScreenOff(int which) { + public long getUahDischargeScreenOff(int which) { return mDischargeScreenOffCounter.getCountLocked(which); } @Override - public long getMahDischargeScreenDoze(int which) { + public long getUahDischargeScreenDoze(int which) { return mDischargeScreenDozeCounter.getCountLocked(which); } @@ -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(); @@ -5427,6 +5427,10 @@ public class BatteryStatsImpl extends BatteryStats { elapsedRealtimeUs, which); } + @Override public Timer getScreenBrightnessTimer(int brightnessBin) { + return mScreenBrightnessTimer[brightnessBin]; + } + @Override public long getInteractiveTime(long elapsedRealtimeUs, int which) { return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -5520,10 +5524,18 @@ public class BatteryStatsImpl extends BatteryStats { elapsedRealtimeUs, which); } + @Override public Timer getPhoneSignalScanningTimer() { + return mPhoneSignalScanningTimer; + } + @Override public int getPhoneSignalStrengthCount(int strengthBin, int which) { return mPhoneSignalStrengthsTimer[strengthBin].getCountLocked(which); } + @Override public Timer getPhoneSignalStrengthTimer(int strengthBin) { + return mPhoneSignalStrengthsTimer[strengthBin]; + } + @Override public long getPhoneDataConnectionTime(int dataType, long elapsedRealtimeUs, int which) { return mPhoneDataConnectionsTimer[dataType].getTotalTimeLocked( @@ -5534,6 +5546,10 @@ public class BatteryStatsImpl extends BatteryStats { return mPhoneDataConnectionsTimer[dataType].getCountLocked(which); } + @Override public Timer getPhoneDataConnectionTimer(int dataType) { + return mPhoneDataConnectionsTimer[dataType]; + } + @Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) { return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -5572,6 +5588,10 @@ public class BatteryStatsImpl extends BatteryStats { return mWifiStateTimer[wifiState].getCountLocked(which); } + @Override public Timer getWifiStateTimer(int wifiState) { + return mWifiStateTimer[wifiState]; + } + @Override public long getWifiSupplStateTime(int state, long elapsedRealtimeUs, int which) { return mWifiSupplStateTimer[state].getTotalTimeLocked( @@ -5582,6 +5602,10 @@ public class BatteryStatsImpl extends BatteryStats { return mWifiSupplStateTimer[state].getCountLocked(which); } + @Override public Timer getWifiSupplStateTimer(int state) { + return mWifiSupplStateTimer[state]; + } + @Override public long getWifiSignalStrengthTime(int strengthBin, long elapsedRealtimeUs, int which) { return mWifiSignalStrengthsTimer[strengthBin].getTotalTimeLocked( @@ -5592,6 +5616,10 @@ public class BatteryStatsImpl extends BatteryStats { return mWifiSignalStrengthsTimer[strengthBin].getCountLocked(which); } + @Override public Timer getWifiSignalStrengthTimer(int strengthBin) { + return mWifiSignalStrengthsTimer[strengthBin]; + } + @Override public ControllerActivityCounter getBluetoothControllerActivity() { return mBluetoothActivity; @@ -9463,7 +9491,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) { @@ -12791,7 +12819,7 @@ public class BatteryStatsImpl extends BatteryStats { mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase, in); - mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null, + mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null, mOnBatteryTimeBase, in); mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in); mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in); 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/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index ad05a5113dff..635eed3fd0ef 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -1051,7 +1051,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { } // Read the bitmap blob. - size_t size = bitmap->getSize(); + size_t size = bitmap->computeByteSize(); android::Parcel::ReadableBlob blob; android::status_t status = p->readBlob(size, &blob); if (status) { @@ -1188,7 +1188,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, p->allowFds() ? "allowed" : "forbidden"); #endif - size_t size = bitmap.getSize(); + size_t size = bitmap.computeByteSize(); android::Parcel::WritableBlob blob; status = p->writeBlob(size, mutableCopy, &blob); if (status) { @@ -1411,7 +1411,7 @@ static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); // the java side has already checked that buffer is large enough - memcpy(abp.pointer(), src, bitmap.getSize()); + memcpy(abp.pointer(), src, bitmap.computeByteSize()); } } @@ -1424,7 +1424,7 @@ static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, if (NULL != dst) { android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); // the java side has already checked that buffer is large enough - memcpy(dst, abp.pointer(), bitmap.getSize()); + memcpy(dst, abp.pointer(), bitmap.computeByteSize()); bitmap.notifyPixelsChanged(); } } diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 64e12b4ec05c..5990d7be9d23 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -174,13 +174,12 @@ public: return false; } - const int64_t size64 = info.getSafeSize64(bitmap->rowBytes()); - if (!sk_64_isS32(size64)) { + const size_t size = info.computeByteSize(bitmap->rowBytes()); + if (size > SK_MaxS32) { ALOGW("bitmap is too large"); return false; } - const size_t size = sk_64_asS32(size64); if (size > mSize) { ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap " "(%zu bytes)", mSize, size); diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 5ea501e5fefd..90cc7bb72bfa 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -645,7 +645,7 @@ bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap) { const int maxHeight = SkTMax(bitmap->height(), mRecycledBitmap->info().height()); const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight); const size_t rowBytes = maxInfo.minRowBytes(); - const size_t bytesNeeded = maxInfo.getSafeSize(rowBytes); + const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes); if (bytesNeeded <= mRecycledBytes) { // Here we take advantage of reconfigure() to reset the rowBytes // of mRecycledBitmap. It is very important that we pass in diff --git a/core/jni/android_app_admin_SecurityLog.cpp b/core/jni/android_app_admin_SecurityLog.cpp index 5c45b4b26a1b..b3bcaa0f7f03 100644 --- a/core/jni/android_app_admin_SecurityLog.cpp +++ b/core/jni/android_app_admin_SecurityLog.cpp @@ -14,183 +14,26 @@ * limitations under the License. */ -#include <fcntl.h> +#include <log/log_id.h> +#include <private/android_logger.h> #include <nativehelper/JNIHelp.h> -#include "core_jni_helpers.h" #include "jni.h" -#include <private/android_logger.h> -// The size of the tag number comes out of the payload size. -#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t)) +#include "core_jni_helpers.h" +#include "eventlog_helper.h" namespace android { -static jclass gCollectionClass; -static jmethodID gCollectionAddID; - -static jclass gEventClass; -static jmethodID gEventInitID; - -static jclass gIntegerClass; -static jfieldID gIntegerValueID; - -static jclass gLongClass; -static jfieldID gLongValueID; - -static jclass gFloatClass; -static jfieldID gFloatValueID; - -static jclass gStringClass; - +constexpr char kSecurityLogEventClass[] = "android/app/admin/SecurityLog$SecurityEvent"; +template class EventLogHelper<log_id_t::LOG_ID_SECURITY, kSecurityLogEventClass>; +using SLog = EventLogHelper<log_id_t::LOG_ID_SECURITY, kSecurityLogEventClass>; static jboolean android_app_admin_SecurityLog_isLoggingEnabled(JNIEnv* env, jobject /* clazz */) { return (bool)__android_log_security(); } -static jint android_app_admin_SecurityLog_writeEvent_String(JNIEnv* env, - jobject /* clazz */, - jint tag, jstring value) { - uint8_t buf[MAX_EVENT_PAYLOAD]; - - // 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. - const char *str = value != NULL ? env->GetStringUTFChars(value, NULL) : "NULL"; - uint32_t len = strlen(str); - size_t max = sizeof(buf) - sizeof(len) - 2; // Type byte, final newline - if (len > max) len = max; - - buf[0] = EVENT_TYPE_STRING; - memcpy(&buf[1], &len, sizeof(len)); - memcpy(&buf[1 + sizeof(len)], str, len); - buf[1 + sizeof(len) + len] = '\n'; - - if (value != NULL) env->ReleaseStringUTFChars(value, str); - return __android_log_security_bwrite(tag, buf, 2 + sizeof(len) + len); -} - -static jint android_app_admin_SecurityLog_writeEvent_Array(JNIEnv* env, jobject clazz, - jint tag, jobjectArray value) { - if (value == NULL) { - return android_app_admin_SecurityLog_writeEvent_String(env, clazz, tag, NULL); - } - - uint8_t buf[MAX_EVENT_PAYLOAD]; - const size_t max = sizeof(buf) - 1; // leave room for final newline - size_t pos = 2; // Save room for type tag & array count - - jsize copied = 0, num = env->GetArrayLength(value); - for (; copied < num && copied < 255; ++copied) { - jobject item = env->GetObjectArrayElement(value, copied); - if (item == NULL || env->IsInstanceOf(item, gStringClass)) { - if (pos + 1 + sizeof(jint) > max) break; - const char *str = item != NULL ? env->GetStringUTFChars((jstring) item, NULL) : "NULL"; - jint len = strlen(str); - if (pos + 1 + sizeof(len) + len > max) len = max - pos - 1 - sizeof(len); - buf[pos++] = EVENT_TYPE_STRING; - memcpy(&buf[pos], &len, sizeof(len)); - memcpy(&buf[pos + sizeof(len)], str, len); - pos += sizeof(len) + len; - if (item != NULL) env->ReleaseStringUTFChars((jstring) item, str); - } else if (env->IsInstanceOf(item, gIntegerClass)) { - jint intVal = env->GetIntField(item, gIntegerValueID); - if (pos + 1 + sizeof(intVal) > max) break; - buf[pos++] = EVENT_TYPE_INT; - memcpy(&buf[pos], &intVal, sizeof(intVal)); - pos += sizeof(intVal); - } else if (env->IsInstanceOf(item, gLongClass)) { - jlong longVal = env->GetLongField(item, gLongValueID); - if (pos + 1 + sizeof(longVal) > max) break; - buf[pos++] = EVENT_TYPE_LONG; - memcpy(&buf[pos], &longVal, sizeof(longVal)); - pos += sizeof(longVal); - } else if (env->IsInstanceOf(item, gFloatClass)) { - jfloat floatVal = env->GetFloatField(item, gFloatValueID); - if (pos + 1 + sizeof(floatVal) > max) break; - buf[pos++] = EVENT_TYPE_FLOAT; - memcpy(&buf[pos], &floatVal, sizeof(floatVal)); - pos += sizeof(floatVal); - } else { - jniThrowException(env, - "java/lang/IllegalArgumentException", - "Invalid payload item type"); - return -1; - } - env->DeleteLocalRef(item); - } - - buf[0] = EVENT_TYPE_LIST; - buf[1] = copied; - buf[pos++] = '\n'; - return __android_log_security_bwrite(tag, buf, pos); -} - -static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) { - struct logger_list *logger_list; - if (startTime) { - logger_list = 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); - } - if (!logger_list) { - jniThrowIOException(env, errno); - return; - } - - if (!android_logger_open(logger_list, LOG_ID_SECURITY)) { - jniThrowIOException(env, errno); - android_logger_list_free(logger_list); - return; - } - - while (1) { - log_msg log_msg; - int ret = android_logger_list_read(logger_list, &log_msg); - - if (ret == 0) { - break; - } - if (ret < 0) { - if (ret == -EINTR) { - continue; - } - if (ret == -EINVAL) { - jniThrowException(env, "java/io/IOException", "Event too short"); - } else if (ret != -EAGAIN) { - jniThrowIOException(env, -ret); // Will throw on return - } - break; - } - - if (log_msg.id() != LOG_ID_SECURITY) { - continue; - } - - jsize len = ret; - jbyteArray array = env->NewByteArray(len); - if (array == NULL) { - break; - } - - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, log_msg.buf, len); - env->ReleaseByteArrayElements(array, bytes, 0); - - jobject event = env->NewObject(gEventClass, gEventInitID, array); - if (event == NULL) { - break; - } - - env->CallBooleanMethod(out, gCollectionAddID, event); - env->DeleteLocalRef(event); - env->DeleteLocalRef(array); - } - - android_logger_list_close(logger_list); -} - static void android_app_admin_SecurityLog_readEvents(JNIEnv* env, jobject /* clazz */, jobject out) { @@ -198,7 +41,7 @@ static void android_app_admin_SecurityLog_readEvents(JNIEnv* env, jobject /* cla jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out); + SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out); } static void android_app_admin_SecurityLog_readEventsSince(JNIEnv* env, jobject /* clazz */, @@ -209,7 +52,7 @@ static void android_app_admin_SecurityLog_readEventsSince(JNIEnv* env, jobject / jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out); + SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out); } static void android_app_admin_SecurityLog_readPreviousEvents(JNIEnv* env, jobject /* clazz */, @@ -219,7 +62,7 @@ static void android_app_admin_SecurityLog_readPreviousEvents(JNIEnv* env, jobjec jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out); + SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out); } static void android_app_admin_SecurityLog_readEventsOnWrapping(JNIEnv* env, jobject /* clazz */, @@ -229,7 +72,8 @@ static void android_app_admin_SecurityLog_readEventsOnWrapping(JNIEnv* env, jobj jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp, out); + SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp, + out); } /* @@ -243,11 +87,11 @@ static const JNINativeMethod gRegisterMethods[] = { }, { "writeEvent", "(ILjava/lang/String;)I", - (void*) android_app_admin_SecurityLog_writeEvent_String + (void*) SLog::writeEventString }, { "writeEvent", "(I[Ljava/lang/Object;)I", - (void*) android_app_admin_SecurityLog_writeEvent_Array + (void*) SLog::writeEventArray }, { "readEvents", "(Ljava/util/Collection;)V", @@ -267,41 +111,8 @@ static const JNINativeMethod gRegisterMethods[] = { }, }; -static struct { const char *name; jclass *clazz; } gClasses[] = { - { "android/app/admin/SecurityLog$SecurityEvent", &gEventClass }, - { "java/lang/Integer", &gIntegerClass }, - { "java/lang/Long", &gLongClass }, - { "java/lang/Float", &gFloatClass }, - { "java/lang/String", &gStringClass }, - { "java/util/Collection", &gCollectionClass }, -}; - -static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = { - { &gIntegerClass, "value", "I", &gIntegerValueID }, - { &gLongClass, "value", "J", &gLongValueID }, - { &gFloatClass, "value", "F", &gFloatValueID }, -}; - -static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = { - { &gEventClass, "<init>", "([B)V", &gEventInitID }, - { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID }, -}; - int register_android_app_admin_SecurityLog(JNIEnv* env) { - for (int i = 0; i < NELEM(gClasses); ++i) { - jclass clazz = FindClassOrDie(env, gClasses[i].name); - *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz); - } - - for (int i = 0; i < NELEM(gFields); ++i) { - *gFields[i].id = GetFieldIDOrDie(env, - *gFields[i].c, gFields[i].name, gFields[i].ft); - } - - for (int i = 0; i < NELEM(gMethods); ++i) { - *gMethods[i].id = GetMethodIDOrDie(env, - *gMethods[i].c, gMethods[i].name, gMethods[i].mt); - } + SLog::Init(env); return RegisterMethodsOrDie( env, 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/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp index 9fd7c4041058..3b5a144a4e61 100644 --- a/core/jni/android_util_EventLog.cpp +++ b/core/jni/android_util_EventLog.cpp @@ -14,214 +14,20 @@ * limitations under the License. */ -#include <fcntl.h> - -#include <log/log_event_list.h> - -#include <log/log.h> +#include <android-base/macros.h> +#include <log/log_id.h> #include <nativehelper/JNIHelp.h> -#include "core_jni_helpers.h" #include "jni.h" -#define UNUSED __attribute__((__unused__)) +#include "core_jni_helpers.h" +#include "eventlog_helper.h" namespace android { -static jclass gCollectionClass; -static jmethodID gCollectionAddID; - -static jclass gEventClass; -static jmethodID gEventInitID; - -static jclass gIntegerClass; -static jfieldID gIntegerValueID; - -static jclass gLongClass; -static jfieldID gLongValueID; - -static jclass gFloatClass; -static jfieldID gFloatValueID; - -static jclass gStringClass; - -/* - * In class android.util.EventLog: - * static native int writeEvent(int tag, int value) - */ -static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env UNUSED, - jobject clazz UNUSED, - jint tag, jint value) -{ - android_log_event_list ctx(tag); - ctx << (int32_t)value; - return ctx.write(); -} - -/* - * In class android.util.EventLog: - * static native int writeEvent(long tag, long value) - */ -static jint android_util_EventLog_writeEvent_Long(JNIEnv* env UNUSED, - jobject clazz UNUSED, - jint tag, jlong value) -{ - android_log_event_list ctx(tag); - ctx << (int64_t)value; - return ctx.write(); -} - -/* - * In class android.util.EventLog: - * static native int writeEvent(long tag, float value) - */ -static jint android_util_EventLog_writeEvent_Float(JNIEnv* env UNUSED, - jobject clazz UNUSED, - jint tag, jfloat value) -{ - android_log_event_list ctx(tag); - ctx << (float)value; - return ctx.write(); -} - -/* - * In class android.util.EventLog: - * static native int writeEvent(int tag, String value) - */ -static jint android_util_EventLog_writeEvent_String(JNIEnv* env, - jobject clazz UNUSED, - jint tag, jstring value) { - 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"; - } - return ctx.write(); -} - -/* - * In class android.util.EventLog: - * static native int writeEvent(long tag, Object... value) - */ -static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz, - jint tag, jobjectArray value) { - android_log_event_list ctx(tag); - - if (value == NULL) { - ctx << "[NULL]"; - return ctx.write(); - } - - 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) { - 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 { - jniThrowException(env, - "java/lang/IllegalArgumentException", - "Invalid payload item type"); - return -1; - } - env->DeleteLocalRef(item); - } - return ctx.write(); -} - -static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime, jobject out) { - struct logger_list *logger_list; - if (startTime) { - logger_list = 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); - } - if (!logger_list) { - jniThrowIOException(env, errno); - return; - } - - if (!android_logger_open(logger_list, LOG_ID_EVENTS)) { - jniThrowIOException(env, errno); - android_logger_list_free(logger_list); - return; - } - - jsize tagLength = env->GetArrayLength(tags); - jint *tagValues = env->GetIntArrayElements(tags, NULL); - - while (1) { - log_msg log_msg; - int ret = android_logger_list_read(logger_list, &log_msg); - - if (ret == 0) { - break; - } - if (ret < 0) { - if (ret == -EINTR) { - continue; - } - if (ret == -EINVAL) { - jniThrowException(env, "java/io/IOException", "Event too short"); - } else if (ret != -EAGAIN) { - jniThrowIOException(env, -ret); // Will throw on return - } - break; - } - - if (log_msg.id() != LOG_ID_EVENTS) { - continue; - } - - int32_t tag = * (int32_t *) log_msg.msg(); - - int found = 0; - for (int i = 0; !found && i < tagLength; ++i) { - found = (tag == tagValues[i]); - } - - if (found) { - jsize len = ret; - jbyteArray array = env->NewByteArray(len); - if (array == NULL) { - break; - } - - jbyte *bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, log_msg.buf, len); - env->ReleaseByteArrayElements(array, bytes, 0); - - jobject event = env->NewObject(gEventClass, gEventInitID, array); - if (event == NULL) { - break; - } - - env->CallBooleanMethod(out, gCollectionAddID, event); - env->DeleteLocalRef(event); - env->DeleteLocalRef(array); - } - } - - android_logger_list_close(logger_list); - - env->ReleaseIntArrayElements(tags, tagValues, 0); -} +constexpr char kEventLogEventClass[] = "android/util/EventLog$Event"; +template class EventLogHelper<log_id_t::LOG_ID_EVENTS, kEventLogEventClass>; +using ELog = EventLogHelper<log_id_t::LOG_ID_EVENTS, kEventLogEventClass>; /* * In class android.util.EventLog: @@ -229,7 +35,7 @@ static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startT * * Reads events from the event log */ -static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED, +static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jintArray tags, jobject out) { @@ -238,7 +44,7 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED, return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out); + ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out); } /* * In class android.util.EventLog: @@ -246,7 +52,7 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED, * * Reads events from the event log, blocking until events after timestamp are to be overwritten. */ -static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz UNUSED, +static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jintArray tags, jlong timestamp, jobject out) { @@ -254,8 +60,8 @@ static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject claz jniThrowNullPointerException(env, NULL); return; } - readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, - tags, timestamp, out); + ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, tags, + timestamp, out); } /* @@ -263,17 +69,11 @@ static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject claz */ static const JNINativeMethod gRegisterMethods[] = { /* name, signature, funcPtr */ - { "writeEvent", "(II)I", (void*) android_util_EventLog_writeEvent_Integer }, - { "writeEvent", "(IJ)I", (void*) android_util_EventLog_writeEvent_Long }, - { "writeEvent", "(IF)I", (void*) android_util_EventLog_writeEvent_Float }, - { "writeEvent", - "(ILjava/lang/String;)I", - (void*) android_util_EventLog_writeEvent_String - }, - { "writeEvent", - "(I[Ljava/lang/Object;)I", - (void*) android_util_EventLog_writeEvent_Array - }, + { "writeEvent", "(II)I", (void*) ELog::writeEventInteger }, + { "writeEvent", "(IJ)I", (void*) ELog::writeEventLong }, + { "writeEvent", "(IF)I", (void*) ELog::writeEventFloat }, + { "writeEvent", "(ILjava/lang/String;)I", (void*) ELog::writeEventString }, + { "writeEvent", "(I[Ljava/lang/Object;)I", (void*) ELog::writeEventArray }, { "readEvents", "([ILjava/util/Collection;)V", (void*) android_util_EventLog_readEvents @@ -284,41 +84,8 @@ static const JNINativeMethod gRegisterMethods[] = { }, }; -static struct { const char *name; jclass *clazz; } gClasses[] = { - { "android/util/EventLog$Event", &gEventClass }, - { "java/lang/Integer", &gIntegerClass }, - { "java/lang/Long", &gLongClass }, - { "java/lang/Float", &gFloatClass }, - { "java/lang/String", &gStringClass }, - { "java/util/Collection", &gCollectionClass }, -}; - -static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = { - { &gIntegerClass, "value", "I", &gIntegerValueID }, - { &gLongClass, "value", "J", &gLongValueID }, - { &gFloatClass, "value", "F", &gFloatValueID }, -}; - -static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = { - { &gEventClass, "<init>", "([B)V", &gEventInitID }, - { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID }, -}; - int register_android_util_EventLog(JNIEnv* env) { - for (int i = 0; i < NELEM(gClasses); ++i) { - jclass clazz = FindClassOrDie(env, gClasses[i].name); - *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz); - } - - for (int i = 0; i < NELEM(gFields); ++i) { - *gFields[i].id = GetFieldIDOrDie(env, - *gFields[i].c, gFields[i].name, gFields[i].ft); - } - - for (int i = 0; i < NELEM(gMethods); ++i) { - *gMethods[i].id = GetMethodIDOrDie(env, - *gMethods[i].c, gMethods[i].name, gMethods[i].mt); - } + ELog::Init(env); return RegisterMethodsOrDie( env, diff --git a/core/jni/eventlog_helper.h b/core/jni/eventlog_helper.h new file mode 100644 index 000000000000..3a05195ebc9e --- /dev/null +++ b/core/jni/eventlog_helper.h @@ -0,0 +1,272 @@ +/* + * 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 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> +#include <log/log_event_list.h> + +#include <log/log.h> + +#include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/ScopedUtfChars.h> +#include "core_jni_helpers.h" +#include "jni.h" + +namespace android { + +template <log_id_t LogID, const char* EventClassDescriptor> +class EventLogHelper { +public: + static void Init(JNIEnv* env) { + struct { const char *name; jclass *clazz; } gClasses[] = { + { EventClassDescriptor, &gEventClass }, + { "java/lang/Integer", &gIntegerClass }, + { "java/lang/Long", &gLongClass }, + { "java/lang/Float", &gFloatClass }, + { "java/lang/String", &gStringClass }, + { "java/util/Collection", &gCollectionClass }, + }; + struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = { + { &gIntegerClass, "value", "I", &gIntegerValueID }, + { &gLongClass, "value", "J", &gLongValueID }, + { &gFloatClass, "value", "F", &gFloatValueID }, + }; + struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = { + { &gEventClass, "<init>", "([B)V", &gEventInitID }, + { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID }, + }; + + for (size_t i = 0; i < NELEM(gClasses); ++i) { + ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, gClasses[i].name)); + *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz.get()); + } + for (size_t i = 0; i < NELEM(gFields); ++i) { + *gFields[i].id = GetFieldIDOrDie(env, + *gFields[i].c, gFields[i].name, gFields[i].ft); + } + + for (size_t i = 0; i < NELEM(gMethods); ++i) { + *gMethods[i].id = GetMethodIDOrDie(env, + *gMethods[i].c, gMethods[i].name, gMethods[i].mt); + } + } + + static jint writeEventInteger(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, + jint tag, jint value) { + android_log_event_list ctx(tag); + ctx << (int32_t)value; + return ctx.write(LogID); + } + static jint writeEventLong(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, + jint tag, jlong value) { + android_log_event_list ctx(tag); + ctx << (int64_t)value; + return ctx.write(LogID); + } + static jint writeEventFloat(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED, + jint tag, jfloat value) { + android_log_event_list ctx(tag); + ctx << (float)value; + return ctx.write(LogID); + } + static jint writeEventString(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag, + jstring value) { + 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. + 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 == nullptr) { + ctx << "[NULL]"; + return ctx.write(LogID); + } + + jsize copied = 0, num = env->GetArrayLength(value); + for (; copied < num && copied < 255; ++copied) { + if (ctx.status()) break; + ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(value, copied)); + if (item == nullptr) { + ctx << "NULL"; + } 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; + } + } + return ctx.write(LogID); + } + + static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) { + readEvents(env, loggerMode, nullptr, startTime, out); + } + + static void readEvents(JNIEnv* env, int loggerMode, jintArray jTags, jlong startTime, + jobject out) { + std::unique_ptr<struct logger_list, decltype(&android_logger_list_close)> logger_list( + nullptr, android_logger_list_close); + if (startTime) { + logger_list.reset(android_logger_list_alloc_time(loggerMode, + log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0)); + } else { + logger_list.reset(android_logger_list_alloc(loggerMode, 0, 0)); + } + if (!logger_list) { + jniThrowIOException(env, errno); + return; + } + + if (!android_logger_open(logger_list.get(), LogID)) { + jniThrowIOException(env, errno); + return; + } + + ScopedIntArrayRO tags(env); + if (jTags != nullptr) { + tags.reset(jTags); + } + + while (1) { + log_msg log_msg; + int ret = android_logger_list_read(logger_list.get(), &log_msg); + + if (ret == 0) { + return; + } + if (ret < 0) { + if (ret == -EINTR) { + continue; + } + if (ret == -EINVAL) { + jniThrowException(env, "java/io/IOException", "Event too short"); + } else if (ret != -EAGAIN) { + jniThrowIOException(env, -ret); // Will throw on return + } + return; + } + + if (log_msg.id() != LogID) { + continue; + } + + int32_t tag = * (int32_t *) log_msg.msg(); + + if (jTags != nullptr) { + bool found = false; + for (size_t i = 0; !found && i < tags.size(); ++i) { + found = (tag == tags[i]); + } + if (!found) { + continue; + } + } + + jsize len = ret; + ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(len)); + if (array == nullptr) { + return; + } + + { + ScopedByteArrayRW bytes(env, array.get()); + memcpy(bytes.get(), log_msg.buf, len); + } + + ScopedLocalRef<jobject> event(env, + env->NewObject(gEventClass, gEventInitID, array.get())); + if (event == nullptr) { + return; + } + + env->CallBooleanMethod(out, gCollectionAddID, event.get()); + if (env->ExceptionCheck() == JNI_TRUE) { + return; + } + } + } + +private: + static jclass gCollectionClass; + static jmethodID gCollectionAddID; + + static jclass gEventClass; + static jmethodID gEventInitID; + + static jclass gIntegerClass; + static jfieldID gIntegerValueID; + + static jclass gLongClass; + static jfieldID gLongValueID; + + static jclass gFloatClass; + static jfieldID gFloatValueID; + + static jclass gStringClass; +}; + +// Explicit instantiation declarations. +template <log_id_t LogID, const char* EventClassDescriptor> +jclass EventLogHelper<LogID, EventClassDescriptor>::gCollectionClass; +template <log_id_t LogID, const char* EventClassDescriptor> +jmethodID EventLogHelper<LogID, EventClassDescriptor>::gCollectionAddID; + +template <log_id_t LogID, const char* EventClassDescriptor> +jclass EventLogHelper<LogID, EventClassDescriptor>::gEventClass; +template <log_id_t LogID, const char* EventClassDescriptor> +jmethodID EventLogHelper<LogID, EventClassDescriptor>::gEventInitID; + +template <log_id_t LogID, const char* EventClassDescriptor> +jclass EventLogHelper<LogID, EventClassDescriptor>::gIntegerClass; +template <log_id_t LogID, const char* EventClassDescriptor> +jfieldID EventLogHelper<LogID, EventClassDescriptor>::gIntegerValueID; + +template <log_id_t LogID, const char* EventClassDescriptor> +jclass EventLogHelper<LogID, EventClassDescriptor>::gLongClass; +template <log_id_t LogID, const char* EventClassDescriptor> +jfieldID EventLogHelper<LogID, EventClassDescriptor>::gLongValueID; + +template <log_id_t LogID, const char* EventClassDescriptor> +jclass EventLogHelper<LogID, EventClassDescriptor>::gFloatClass; +template <log_id_t LogID, const char* EventClassDescriptor> +jfieldID EventLogHelper<LogID, EventClassDescriptor>::gFloatValueID; + +template <log_id_t LogID, const char* EventClassDescriptor> +jclass EventLogHelper<LogID, EventClassDescriptor>::gStringClass; + +} // namespace android + +#endif // FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_ diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto index 86e31d0940db..8d850384ffe3 100644 --- a/core/proto/android/os/batterystats.proto +++ b/core/proto/android/os/batterystats.proto @@ -20,6 +20,8 @@ option java_multiple_files = true; package android.os; +import "frameworks/base/core/proto/android/telephony/signalstrength.proto"; + message BatteryStatsProto { int32 report_version = 1; int64 parcel_version = 2; @@ -55,6 +57,383 @@ message ControllerActivityProto { } message SystemProto { + message Battery { + // Wall clock time when the data collection started. + // In case of device time manually reset by users: + // start_clock_time_ms keeps the same value in the current collection + // period and changes for later collection periods. + int64 start_clock_time_ms = 1; + // #times the device has been started since start_clock_time_millis. + int64 start_count = 2; + // Total realtime duration (= SINCE_UNPLUGGED battery_realtime_millis.) + int64 total_realtime_ms = 3; + int64 total_uptime_ms = 4; + // Realtime duration on battery. + int64 battery_realtime_ms = 5; + // Uptime duration (i.e., not suspend). + // Uptime is anytime the CPUs were on. The radio and Wifi chip + // can be running while the CPUs are off. + int64 battery_uptime_ms = 6; + // Total realtime duration measured with screen off or dozing. + int64 screen_off_realtime_ms = 7; + // Total uptime duration measured with screen off or dozing. + int64 screen_off_uptime_ms = 8; + // Total time the screen was dozing while the device was running on battery. + // For historical reasons, screen_doze_duration_msec is a subset of + // screen_off_realtime_msec. + int64 screen_doze_duration_ms = 9; + // The estimated real battery capacity, which may be less than the declared + // battery capacity (for example, because of battery aging). This field is + // less reliable than min(max)_learned_battery_capacity_uah, use those two + // fields whenever possible. + int64 estimated_battery_capacity_mah = 10; + // The minimum learned battery capacity in uAh. + int64 min_learned_battery_capacity_uah = 11; + // The maximum learned battery capacity in uAh. + int64 max_learned_battery_capacity_uah = 12; + }; + Battery battery = 1; + + message BatteryDischarge { + // Discharged battery percentage points since the stats were last reset + // after charging (lower bound approximation). + int32 lower_bound_since_charge = 1; + // Upper bound approximation. + int32 upper_bound_since_charge = 2; + // Discharged points while screen is on. + int32 screen_on_since_charge = 3; + // Discharged points while screen is off. + int32 screen_off_since_charge = 4; + // Discharged points while screen was dozing. For historical reasons, + // screen_doze_since_charge is a subset of screen_off_since_charge. + int32 screen_doze_since_charge = 5; + // Total amount of battery discharged in mAh. This will only be non-zero for + // devices that report battery discharge via a coulomb counter. + int64 total_mah = 6; + // Total amount of battery discharged while the screen was off in mAh. + // This will only be non-zero for devices that report battery discharge + // via a coulomb counter. + int64 total_mah_screen_off = 7; + // Total amount of battery discharged while the screen was dozing in mAh. + // This will only be non-zero for devices that report battery discharge + // via a coulomb counter. For historical reasons, total_mah_screen_doze is + // a subset of total_mah_screen_off. + int64 total_mah_screen_doze = 8; + }; + BatteryDischarge battery_discharge = 2; + + oneof time_remaining { + // Approximation for how much time remains until the battery is fully + // charged. The device will print -1 if there wasn't enough data to + // calculate an estimate, or if the battery is currently discharging. + int64 charge_time_remaining_ms = 3; + // Approximation for how much time remains until the battery is fully + // discharged. The device will print -1 if there wasn't enough data to + // calculate an estimate, or if the battery is currently charging. + int64 discharge_time_remaining_ms = 4; + } + + // BatteryLevelStep tracks data for which conditions were continuously held for + // the entire duration. Field for which the conditions were not consistent + // for the entire duration should be marked MIXED. + message BatteryLevelStep { + // How long the battery was at the current level. + int64 duration_ms = 1; + // Battery level + int32 level = 2; + + // State of the display. A special enum is used rather than + // DisplayProto.State because a MIXED value needs to be in the enum, and + // batterystats doesn't care about all of the different display states. + enum DisplayState { + DS_MIXED = 0; + DS_ON = 1; + DS_OFF = 2; + DS_DOZE = 3; + DS_DOZE_SUSPEND = 4; + // Any display state error that comes through should be sent to hackbod@. + DS_ERROR = 5; + } + // The state of the display for the entire battery level step. MIXED is used + // if there were multiple states for this step. + DisplayState display_state = 3; + + // Indicates status in power save mode. + enum PowerSaveMode { + PSM_MIXED = 0; + PSM_ON = 1; + PSM_OFF = 2; + } + // Battery Saver mode for the entire battery level step. MIXED is used + // if there were multiple states for this step. + PowerSaveMode power_save_mode = 4; + + // Indicates status in idle mode. + enum IdleMode { + IM_MIXED = 0; + IM_ON = 2; + IM_OFF = 3; + } + // Doze mode for the entire battery level step. MIXED is used if there were + // multiple states for this step. + IdleMode idle_mode = 5; + }; + // Battery level steps when the device was charging. + repeated BatteryLevelStep charge_step = 5; + // Battery level steps when the device was discharging. + repeated BatteryLevelStep discharge_step = 6; + + // All CPU frequencies of the device. + repeated int64 cpu_frequency = 7; + + message DataConnection { + enum Name { + NONE = 0; + GPRS = 1; + EDGE = 2; + UMTS = 3; + CDMA = 4; + EVDO_0 = 5; + EVDO_A = 6; + ONE_X_RTT = 7; // 1xRTT. + HSDPA = 8; + HSUPA = 9; + HSPA = 10; + IDEN = 11; + EVDO_B = 12; + LTE = 13; + EHRPD = 14; + HSPAP = 15; + OTHER = 16; + }; + Name name = 1; + TimerProto total = 2; + }; + repeated DataConnection data_connection = 8; + + ControllerActivityProto global_bluetooth_controller = 9; + ControllerActivityProto global_modem_controller = 10; + ControllerActivityProto global_wifi_controller = 11; + + message GlobalNetwork { + // Total Bytes received on mobile connections. + int64 mobile_bytes_rx = 1; + // Total Bytes transmitted on mobile connections. + int64 mobile_bytes_tx = 2; + // Total Bytes received on wifi connections. + int64 wifi_bytes_rx = 3; + // Total Bytes transmitted on wifi connections. + int64 wifi_bytes_tx = 4; + // Total Packets received on mobile connections. + int64 mobile_packets_rx = 5; + // Total Packets transmitted on mobile connections. + int64 mobile_packets_tx = 6; + // Total Packets received on wifi connections. + int64 wifi_packets_rx = 7; + // Total Packets transmitted on wifi connections. + int64 wifi_packets_tx = 8; + // Total Bytes received on bluetooth connections. + int64 bt_bytes_rx = 9; + // Total Bytes transmitted on bluetooth connections. + int64 bt_bytes_tx = 10; + }; + GlobalNetwork global_network = 12; + + message GlobalWifi { + // The amount of time that wifi has been on while the device was running on + // battery. + int64 on_duration_ms = 1; + // The amount of time that wifi has been on and the driver has been in the + // running state while the device was running on battery. + int64 running_duration_ms = 2; + } + GlobalWifi global_wifi = 13; + + // Kernel wakelock metrics are only recorded when the device is unplugged + // *and* the screen is off. + message KernelWakelock { + string name = 1; + // Kernel wakelock stats aren't apportioned across all kernel wakelocks (as + // app wakelocks stats are). + TimerProto total = 2; + // The kernel doesn't have the data to enable printing out current and max + // durations. + }; + repeated KernelWakelock kernel_wakelock = 14; + + message Misc { + int64 screen_on_duration_ms = 1; + int64 phone_on_duration_ms = 2; + int64 full_wakelock_total_duration_ms = 3; + // The total elapsed time that a partial wakelock was held. This duration + // does not double count wakelocks held at the same time. + int64 partial_wakelock_total_duration_ms = 4; + int64 mobile_radio_active_duration_ms = 5; + // The time that is the difference between the mobile radio time we saw + // based on the elapsed timestamp when going down vs. the given time stamp + // from the radio. + int64 mobile_radio_active_adjusted_time_ms = 6; + int32 mobile_radio_active_count = 7; + // The amount of time that the mobile network has been active (in a high + // power state) but not being able to blame on an app. + int32 mobile_radio_active_unknown_duration_ms = 8; + // Total amount of time the device was in the interactive state. + int64 interactive_duration_ms = 9; + int64 battery_saver_mode_enabled_duration_ms = 10; + int32 num_connectivity_changes = 11; + // Amount of time the device was in deep Doze. + int64 deep_doze_enabled_duration_ms = 12; + // How many times the device went into deep Doze mode. + int32 deep_doze_count = 13; + // Amount of time the device was idling in deep Doze. Idling time + // encompasses "doze" time and the maintenance windows that allow apps to + // operate. + int64 deep_doze_idling_duration_ms = 14; + // How many times the device idling for deep Doze mode. + int32 deep_doze_idling_count = 15; + int64 longest_deep_doze_duration_ms = 16; + // Amount of time the device was in Doze Light. + int64 light_doze_enabled_duration_ms = 17; + // How many times the device went into Doze Light mode. + int32 light_doze_count = 18; + // Amount of time the device was idling in Doze Light. Idling time + // encompasses "doze" time and the maintenance windows that allow apps to + // operate. + int64 light_doze_idling_duration_ms = 19; + // How many times the device idling for Doze Light mode. + int32 light_doze_idling_count = 20; + int64 longest_light_doze_duration_ms = 21; + } + Misc misc = 15; + + message PhoneSignalStrength { + android.telephony.SignalStrengthProto.StrengthName name = 1; + TimerProto total = 2; + }; + repeated PhoneSignalStrength phone_signal_strength = 16; + + message PowerUseItem { + enum Sipper { + UNKNOWN_SIPPER = 0; + IDLE = 1; + CELL = 2; + PHONE = 3; + WIFI = 4; + BLUETOOTH = 5; + FLASHLIGHT = 6; + SCREEN = 7; + USER = 8; + UNACCOUNTED = 9; + OVERCOUNTED = 10; + CAMERA = 11; + MEMORY = 12; + }; + Sipper name = 1; + // UID, only valid for the USER sipper. + int32 uid = 2; + // Estimated power use in mAh. + double computed_power_mah = 3; + // Starting in Oreo, Battery Settings has two modes to display the battery + // info. The first is "app usage list". In this mode, items with should_hide + // enabled are hidden. + bool should_hide = 4; + // Smeared power from screen usage. Screen usage power is split and smeared + // among apps, based on activity time. + double screen_power_mah = 5; + // Smeared power using proportional method. Power usage from hidden sippers + // is smeared to all apps proportionally (except for screen usage). + double proportional_smear_mah = 6; + }; + repeated PowerUseItem power_use_item = 17; + + message PowerUseSummary { + double battery_capacity_mah = 1; + double computed_power_mah = 2; + // Lower bound of actual power drained. + double min_drained_power_mah = 3; + // Upper bound of actual power drained. + double max_drained_power_mah = 4; + }; + PowerUseSummary power_use_summary = 18; + + message ResourcePowerManager { + string name = 1; + TimerProto total = 2; + TimerProto screen_off = 3; + } + ResourcePowerManager resource_power_manager = 19; + + message ScreenBrightness { + enum Name { + DARK = 0; // Not screen-off. + DIM = 1; + MEDIUM = 2; + LIGHT = 3; + BRIGHT = 4; + }; + Name name = 1; + TimerProto total = 2; + }; + repeated ScreenBrightness screen_brightness = 20; + + // Duration and number of times trying to acquire a signal + TimerProto signal_scanning = 21; + + message WakeupReason { + string name = 1; + TimerProto total = 2; + }; + repeated WakeupReason wakeup_reason = 22; + + message WifiSignalStrength { + enum Name { + NONE = 0; + POOR = 1; + MODERATE = 2; + GOOD = 3; + GREAT = 4; + }; + Name name = 1; + TimerProto total = 2; + }; + repeated WifiSignalStrength wifi_signal_strength = 23; + + message WifiState { + enum Name { + OFF = 0; + OFF_SCANNING = 1; + ON_NO_NETWORKS = 2; + ON_DISCONNECTED = 3; + ON_CONNECTED_STA = 4; + ON_CONNECTED_P2P = 5; + ON_CONNECTED_STA_P2P = 6; + SOFT_AP = 7; + }; + Name name = 1; + TimerProto total = 2; + }; + repeated WifiState wifi_state = 24; + + message WifiSupplicantState { + enum Name { + INVALID = 0; + DISCONNECTED = 1; + INTERFACE_DISABLED = 2; + INACTIVE = 3; + SCANNING = 4; + AUTHENTICATING = 5; + ASSOCIATING = 6; + ASSOCIATED = 7; + FOUR_WAY_HANDSHAKE = 8; + GROUP_HANDSHAKE = 9; + COMPLETED = 10; + DORMANT = 11; + UNINITIALIZED = 12; + }; + Name name = 1; + TimerProto total = 2; + }; + repeated WifiSupplicantState wifi_supplicant_state = 25; } message TimerProto { diff --git a/core/proto/android/telephony/signalstrength.proto b/core/proto/android/telephony/signalstrength.proto new file mode 100644 index 000000000000..ff230cba6a57 --- /dev/null +++ b/core/proto/android/telephony/signalstrength.proto @@ -0,0 +1,35 @@ +/* + * 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. + */ + +syntax = "proto3"; + +option java_package = "android.telephony"; +option java_multiple_files = true; + +package android.telephony; + +/** + * An android.telephony.SignalStrength object. + */ +message SignalStrengthProto { + enum StrengthName { + SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; + SIGNAL_STRENGTH_POOR = 1; + SIGNAL_STRENGTH_MODERATE = 2; + SIGNAL_STRENGTH_GOOD = 3; + SIGNAL_STRENGTH_GREAT = 4; + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6a9afbcae142..ff579a6e07d8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -963,7 +963,7 @@ android:permissionGroup="android.permission-group.MICROPHONE" android:label="@string/permlab_recordAudio" android:description="@string/permdesc_recordAudio" - android:protectionLevel="dangerous"/> + android:protectionLevel="dangerous|instant"/> <!-- ====================================================================== --> <!-- Permissions for accessing the UCE Service --> 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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 6a3a97392955..902b872e9c74 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -253,12 +253,14 @@ applications that come with the platform </privapp-permissions> <privapp-permissions package="com.android.shell"> + <permission name="android.permission.ACCESS_LOWPAN_STATE"/> <permission name="android.permission.BACKUP"/> <permission name="android.permission.BATTERY_STATS"/> <permission name="android.permission.BIND_APPWIDGET"/> <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> <permission name="android.permission.CHANGE_CONFIGURATION"/> <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" /> + <permission name="android.permission.CHANGE_LOWPAN_STATE"/> <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/> <permission name="android.permission.CLEAR_APP_CACHE"/> <permission name="android.permission.CONNECTIVITY_INTERNAL"/> @@ -282,6 +284,7 @@ applications that come with the platform <permission name="android.permission.MOVE_PACKAGE"/> <permission name="android.permission.PACKAGE_USAGE_STATS" /> <permission name="android.permission.READ_FRAME_BUFFER"/> + <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/> <permission name="android.permission.REAL_GET_TASKS"/> <permission name="android.permission.REGISTER_CALL_PROVIDER"/> <permission name="android.permission.REGISTER_CONNECTION_MANAGER"/> diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java index ed417b97bbeb..a675eaf85297 100644 --- a/graphics/java/android/graphics/drawable/RippleForeground.java +++ b/graphics/java/android/graphics/drawable/RippleForeground.java @@ -169,13 +169,13 @@ class RippleForeground extends RippleComponent { final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1); tweenRadius.setAutoCancel(true); tweenRadius.setDuration(duration); - tweenRadius.setInterpolator(LINEAR_INTERPOLATOR); + tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR); tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY); final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1); tweenOrigin.setAutoCancel(true); tweenOrigin.setDuration(duration); - tweenOrigin.setInterpolator(LINEAR_INTERPOLATOR); + tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR); tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY); final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1); diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk index a5348169513f..2a2b087dc032 100644 --- a/libs/protoutil/Android.mk +++ b/libs/protoutil/Android.mk @@ -22,15 +22,15 @@ LOCAL_CFLAGS := \ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter LOCAL_SHARED_LIBRARIES := \ - libbinder \ + libcutils \ liblog \ - libutils LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include LOCAL_SRC_FILES := \ src/EncodedBuffer.cpp \ + src/ProtoOutputStream.cpp \ src/protobuf.cpp \ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h index cf096091c055..e568e4cf02fc 100644 --- a/libs/protoutil/include/android/util/EncodedBuffer.h +++ b/libs/protoutil/include/android/util/EncodedBuffer.h @@ -52,10 +52,10 @@ public: size_t index() const; size_t offset() const; - void move(size_t amt); - inline void move() { move(1); }; + Pointer* move(size_t amt); + inline Pointer* move() { return move(1); }; + Pointer* rewind(); - void rewind(); Pointer copy() const; private: @@ -88,15 +88,71 @@ public: size_t currentToWrite(); /** - * Write a varint into a vector. Return the size of the varint. + * Write a single byte to the buffer. */ - size_t writeRawVarint(uint32_t val); + void writeRawByte(uint8_t val); + + /** + * Write a varint32 into the buffer. Return the size of the varint. + */ + size_t writeRawVarint32(uint32_t val); + + /** + * Write a varint64 into the buffer. Return the size of the varint. + */ + size_t writeRawVarint64(uint64_t val); + + /** + * Write Fixed32 into the buffer. + */ + void writeRawFixed32(uint32_t val); + + /** + * Write Fixed64 into the buffer. + */ + void writeRawFixed64(uint64_t val); /** * Write a protobuf header. Return the size of the header. */ size_t writeHeader(uint32_t fieldId, uint8_t wireType); + /********************************* Edit APIs ************************************************/ + /** + * Returns the edit pointer. + */ + Pointer* ep(); + + /** + * Read a single byte at ep, and move ep to next byte; + */ + uint8_t readRawByte(); + + /** + * Read varint starting at ep, ep will move to pos of next byte. + */ + uint64_t readRawVarint(); + + /** + * Read 4 bytes starting at ep, ep will move to pos of next byte. + */ + uint32_t readRawFixed32(); + + /** + * Read 8 bytes starting at ep, ep will move to pos of next byte. + */ + uint64_t readRawFixed64(); + + /** + * Edit 4 bytes starting at pos. + */ + void editRawFixed32(size_t pos, uint32_t val); + + /** + * Copy _size_ bytes of data starting at __srcPos__ to wp. + */ + void copy(size_t srcPos, size_t size); + /********************************* Read APIs ************************************************/ class iterator; friend class iterator; @@ -141,9 +197,8 @@ public: /** * Read varint from iterator, the iterator will point to next available byte. - * Return the number of bytes of the varint. */ - uint32_t readRawVarint(); + uint64_t readRawVarint(); private: const EncodedBuffer& mData; @@ -160,6 +215,7 @@ private: vector<uint8_t*> mBuffers; Pointer mWp; + Pointer mEp; inline uint8_t* at(const Pointer& p) const; // helper function to get value }; @@ -167,4 +223,5 @@ private: } // util } // android -#endif // ANDROID_UTIL_ENCODED_BUFFER_H
\ No newline at end of file +#endif // ANDROID_UTIL_ENCODED_BUFFER_H + diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h new file mode 100644 index 000000000000..49ec169b3e5c --- /dev/null +++ b/libs/protoutil/include/android/util/ProtoOutputStream.h @@ -0,0 +1,100 @@ +/* + * 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 ANDROID_UTIL_PROTOOUTPUT_STREAM_H +#define ANDROID_UTIL_PROTOOUTPUT_STREAM_H + +#include <android/util/EncodedBuffer.h> + +#include <stdint.h> +#include <string> + +namespace android { +namespace util { + +/** + * Class to write to a protobuf stream. + * + * Each write method takes an ID code from the protoc generated classes + * and the value to write. To make a nested object, call start + * and then end when you are done. + * + * See the java version implementation (ProtoOutputStream.java) for more infos. + */ +class ProtoOutputStream +{ +public: + ProtoOutputStream(int fd); + ~ProtoOutputStream(); + + /** + * Write APIs for dumping protobuf data. Returns true if the write succeeds. + */ + bool write(uint64_t fieldId, double val); + bool write(uint64_t fieldId, float val); + bool write(uint64_t fieldId, int val); + bool write(uint64_t fieldId, long long val); + bool write(uint64_t fieldId, bool val); + bool write(uint64_t fieldId, std::string val); + bool write(uint64_t fieldId, const char* val); + + /** + * Starts a sub-message write session. + * Returns a token of this write session. + * Must call end(token) when finish write this sub-message. + */ + long long start(uint64_t fieldId); + void end(long long token); + + /** + * Flushes the protobuf data out. + */ + bool flush(); + +private: + EncodedBuffer mBuffer; + int mFd; + size_t mCopyBegin; + bool mCompact; + int mDepth; + int mObjectId; + long long mExpectedObjectToken; + + inline void writeDoubleImpl(uint32_t id, double val); + inline void writeFloatImpl(uint32_t id, float val); + inline void writeInt64Impl(uint32_t id, long long val); + inline void writeInt32Impl(uint32_t id, int val); + inline void writeUint64Impl(uint32_t id, uint64_t val); + inline void writeUint32Impl(uint32_t id, uint32_t val); + inline void writeFixed64Impl(uint32_t id, uint64_t val); + inline void writeFixed32Impl(uint32_t id, uint32_t val); + inline void writeSFixed64Impl(uint32_t id, long long val); + inline void writeSFixed32Impl(uint32_t id, int val); + inline void writeZigzagInt64Impl(uint32_t id, long long val); + inline void writeZigzagInt32Impl(uint32_t id, int val); + inline void writeEnumImpl(uint32_t id, int val); + inline void writeBoolImpl(uint32_t id, bool val); + inline void writeUtf8StringImpl(uint32_t id, const char* val, size_t size); + + bool compact(); + size_t editEncodedSize(size_t rawSize); + bool compactSize(size_t rawSize); +}; + +} +} + +#endif // ANDROID_UTIL_PROTOOUTPUT_STREAM_H
\ No newline at end of file diff --git a/libs/protoutil/include/android/util/protobuf.h b/libs/protoutil/include/android/util/protobuf.h index f4e8d092ba52..ca45e263b20e 100644 --- a/libs/protoutil/include/android/util/protobuf.h +++ b/libs/protoutil/include/android/util/protobuf.h @@ -24,6 +24,9 @@ namespace util { using namespace std; +const int FIELD_ID_SHIFT = 3; +const uint8_t WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1; + const uint8_t WIRE_TYPE_VARINT = 0; const uint8_t WIRE_TYPE_FIXED64 = 1; const uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; @@ -35,16 +38,20 @@ const uint8_t WIRE_TYPE_FIXED32 = 5; uint8_t read_wire_type(uint32_t varint); /** - * read field id from varint, it is varint >> 3; + * Read field id from varint, it is varint >> 3; */ uint32_t read_field_id(uint32_t varint); /** + * Get the size of a varint. + */ +size_t get_varint_size(uint64_t varint); + +/** * Write a varint into the buffer. Return the next position to write at. - * There must be 10 bytes in the buffer. The same as - * EncodedBuffer.writeRawVarint32 + * There must be 10 bytes in the buffer. */ -uint8_t* write_raw_varint(uint8_t* buf, uint32_t val); +uint8_t* write_raw_varint(uint8_t* buf, uint64_t val); /** * Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp index 84dc5b6d7852..435ae8836217 100644 --- a/libs/protoutil/src/EncodedBuffer.cpp +++ b/libs/protoutil/src/EncodedBuffer.cpp @@ -15,6 +15,7 @@ */ #include <android/util/EncodedBuffer.h> +#include <android/util/protobuf.h> #include <stdlib.h> @@ -52,19 +53,21 @@ EncodedBuffer::Pointer::offset() const return mOffset; } -void +EncodedBuffer::Pointer* EncodedBuffer::Pointer::move(size_t amt) { size_t newOffset = mOffset + amt; mIndex += newOffset / mChunkSize; mOffset = newOffset % mChunkSize; + return this; } -void +EncodedBuffer::Pointer* EncodedBuffer::Pointer::rewind() { mIndex = 0; mOffset = 0; + return this; } EncodedBuffer::Pointer @@ -86,6 +89,7 @@ EncodedBuffer::EncodedBuffer(size_t chunkSize) { mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize; mWp = Pointer(mChunkSize); + mEp = Pointer(mChunkSize); } EncodedBuffer::~EncodedBuffer() @@ -137,28 +141,136 @@ EncodedBuffer::currentToWrite() return mChunkSize - mWp.offset(); } +void +EncodedBuffer::writeRawByte(uint8_t val) +{ + *writeBuffer() = val; + mWp.move(); +} + size_t -EncodedBuffer::writeRawVarint(uint32_t val) +EncodedBuffer::writeRawVarint64(uint64_t val) { size_t size = 0; while (true) { size++; if ((val & ~0x7F) == 0) { - *writeBuffer() = (uint8_t) val; - mWp.move(); + writeRawByte((uint8_t) val); return size; } else { - *writeBuffer() = (uint8_t)((val & 0x7F) | 0x80); - mWp.move(); + writeRawByte((uint8_t)((val & 0x7F) | 0x80)); val >>= 7; } } } size_t +EncodedBuffer::writeRawVarint32(uint32_t val) +{ + uint64_t v =(uint64_t)val; + return writeRawVarint64(v); +} + +void +EncodedBuffer::writeRawFixed32(uint32_t val) +{ + writeRawByte((uint8_t) val); + writeRawByte((uint8_t) (val>>8)); + writeRawByte((uint8_t) (val>>16)); + writeRawByte((uint8_t) (val>>24)); +} + +void +EncodedBuffer::writeRawFixed64(uint64_t val) +{ + writeRawByte((uint8_t) val); + writeRawByte((uint8_t) (val>>8)); + writeRawByte((uint8_t) (val>>16)); + writeRawByte((uint8_t) (val>>24)); + writeRawByte((uint8_t) (val>>32)); + writeRawByte((uint8_t) (val>>40)); + writeRawByte((uint8_t) (val>>48)); + writeRawByte((uint8_t) (val>>56)); +} + +size_t EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType) { - return writeRawVarint((fieldId << 3) | wireType); + return writeRawVarint32((fieldId << FIELD_ID_SHIFT) | wireType); +} + +/******************************** Edit APIs ************************************************/ +EncodedBuffer::Pointer* +EncodedBuffer::ep() +{ + return &mEp; +} + +uint8_t +EncodedBuffer::readRawByte() +{ + uint8_t val = *at(mEp); + mEp.move(); + return val; +} + +uint64_t +EncodedBuffer::readRawVarint() +{ + uint64_t val = 0, shift = 0; + size_t start = mEp.pos(); + while (true) { + uint8_t byte = readRawByte(); + val += (byte & 0x7F) << shift; + if ((byte & 0x80) == 0) break; + shift += 7; + } + return val; +} + +uint32_t +EncodedBuffer::readRawFixed32() +{ + uint32_t val = 0; + for (auto i=0; i<32; i+=8) { + val += (uint32_t)readRawByte() << i; + } + return val; +} + +uint64_t +EncodedBuffer::readRawFixed64() +{ + uint64_t val = 0; + for (auto i=0; i<64; i+=8) { + val += (uint64_t)readRawByte() << i; + } + return val; +} + +void +EncodedBuffer::editRawFixed32(size_t pos, uint32_t val) +{ + size_t oldPos = mEp.pos(); + mEp.rewind()->move(pos); + for (auto i=0; i<32; i+=8) { + *at(mEp) = (uint8_t) (val >> i); + mEp.move(); + } + mEp.rewind()->move(oldPos); +} + +void +EncodedBuffer::copy(size_t srcPos, size_t size) +{ + if (size == 0) return; + Pointer cp(mChunkSize); + cp.move(srcPos); + + while (cp.pos() < srcPos + size) { + writeRawByte(*at(cp)); + cp.move(); + } } /********************************* Read APIs ************************************************/ @@ -220,10 +332,10 @@ EncodedBuffer::iterator::next() return res; } -uint32_t +uint64_t EncodedBuffer::iterator::readRawVarint() { - uint32_t val = 0, shift = 0; + uint64_t val = 0, shift = 0; while (true) { uint8_t byte = next(); val += (byte & 0x7F) << shift; diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp new file mode 100644 index 000000000000..e9ca0dcb1093 --- /dev/null +++ b/libs/protoutil/src/ProtoOutputStream.cpp @@ -0,0 +1,652 @@ +/* + * 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 "libprotoutil" + +#include <android/util/protobuf.h> +#include <android/util/ProtoOutputStream.h> +#include <cutils/log.h> +#include <cstring> + +namespace android { +namespace util { + +/** + * Position of the field type in a (long long) fieldId. + */ +const uint64_t FIELD_TYPE_SHIFT = 32; + +/** + * Mask for the field types stored in a fieldId. Leaves a whole + * byte for future expansion, even though there are currently only 17 types. + */ +const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT; + +const uint64_t FIELD_TYPE_UNKNOWN = 0; +const uint64_t TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire. +const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire. +const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT64 if negative + // values are likely. +const uint64_t TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire. +const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT32 if negative + // values are likely. +const uint64_t TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire. +const uint64_t TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire. +const uint64_t TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire. +const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text. +const uint64_t TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated. +const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message. + +const uint64_t TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array. +const uint64_t TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire +const uint64_t TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire +const uint64_t TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire +const uint64_t TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire +const uint64_t TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire +const uint64_t TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire + +// +// FieldId flags for whether the field is single, repeated or packed. +// TODO: packed is not supported yet. +// +const uint64_t FIELD_COUNT_SHIFT = 40; +const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_UNKNOWN = 0; +const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT; +const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT; + +ProtoOutputStream::ProtoOutputStream(int fd) + :mBuffer(), + mFd(fd), + mCopyBegin(0), + mCompact(false), + mDepth(0), + mObjectId(0), + mExpectedObjectToken(0LL) +{ +} + +ProtoOutputStream::~ProtoOutputStream() +{ +} + +bool +ProtoOutputStream::write(uint64_t fieldId, double val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case TYPE_INT32: writeInt32Impl(id, (int)val); break; + case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + default: + ALOGW("Field type %d is not supported when writing double val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool +ProtoOutputStream::write(uint64_t fieldId, float val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case TYPE_INT32: writeInt32Impl(id, (int)val); break; + case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + default: + ALOGW("Field type %d is not supported when writing float val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool +ProtoOutputStream::write(uint64_t fieldId, int val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case TYPE_INT32: writeInt32Impl(id, (int)val); break; + case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case TYPE_ENUM: writeEnumImpl(id, (int)val); break; + case TYPE_BOOL: writeBoolImpl(id, val != 0); break; + default: + ALOGW("Field type %d is not supported when writing int val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool +ProtoOutputStream::write(uint64_t fieldId, long long val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break; + case TYPE_FLOAT: writeFloatImpl(id, (float)val); break; + case TYPE_INT64: writeInt64Impl(id, (long long)val); break; + case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break; + case TYPE_INT32: writeInt32Impl(id, (int)val); break; + case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break; + case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break; + case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break; + case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break; + case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break; + case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break; + case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break; + case TYPE_ENUM: writeEnumImpl(id, (int)val); break; + case TYPE_BOOL: writeBoolImpl(id, val != 0); break; + default: + ALOGW("Field type %d is not supported when writing long long val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } + return true; +} + +bool +ProtoOutputStream::write(uint64_t fieldId, bool val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case TYPE_BOOL: + writeBoolImpl(id, val); + return true; + default: + ALOGW("Field type %d is not supported when writing bool val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } +} + +bool +ProtoOutputStream::write(uint64_t fieldId, string val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + switch (fieldId & FIELD_TYPE_MASK) { + case TYPE_STRING: + writeUtf8StringImpl(id, val.c_str(), val.size()); + return true; + default: + ALOGW("Field type %d is not supported when writing string val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } +} + +bool +ProtoOutputStream::write(uint64_t fieldId, const char* val) +{ + if (mCompact) return false; + const uint32_t id = (uint32_t)fieldId; + int size = 0; + while (val[size] != '\0') size++; + switch (fieldId & FIELD_TYPE_MASK) { + case TYPE_STRING: + writeUtf8StringImpl(id, val, size); + return true; + default: + ALOGW("Field type %d is not supported when writing char[] val.", + (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT)); + return false; + } +} + +/** + * Make a token. + * Bits 61-63 - tag size (So we can go backwards later if the object had not data) + * - 3 bits, max value 7, max value needed 5 + * Bit 60 - true if the object is repeated + * Bits 59-51 - depth (For error checking) + * - 9 bits, max value 512, when checking, value is masked (if we really + * are more than 512 levels deep) + * Bits 32-50 - objectId (For error checking) + * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap + * because of the overflow, and only the tokens are compared. + * Bits 0-31 - offset of the first size field in the buffer. + */ +long long +makeToken(int tagSize, bool repeated, int depth, int objectId, int sizePos) { + return ((0x07L & (long long)tagSize) << 61) + | (repeated ? (1LL << 60) : 0) + | (0x01ffL & (long long)depth) << 51 + | (0x07ffffL & (long long)objectId) << 32 + | (0x0ffffffffL & (long long)sizePos); +} + +/** + * Get the encoded tag size from the token. + */ +static int getTagSizeFromToken(long long token) { + return (int)(0x7 & (token >> 61)); +} + +/** + * Get the nesting depth of startObject calls from the token. + */ +static int getDepthFromToken(long long token) { + return (int)(0x01ff & (token >> 51)); +} + +/** + * Get the location of the childRawSize (the first 32 bit size field) in this object. + */ +static int getSizePosFromToken(long long token) { + return (int)token; +} + +long long +ProtoOutputStream::start(uint64_t fieldId) +{ + if ((fieldId & FIELD_TYPE_MASK) != TYPE_MESSAGE) { + ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId); + return 0; + } + + uint32_t id = (uint32_t)fieldId; + mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED); + + size_t sizePos = mBuffer.wp()->pos(); + + mDepth++; + mObjectId++; + mBuffer.writeRawFixed64(mExpectedObjectToken); // push previous token into stack. + + mExpectedObjectToken = makeToken(get_varint_size(id), + (bool)(fieldId & FIELD_COUNT_REPEATED), mDepth, mObjectId, sizePos); + return mExpectedObjectToken; +} + +void +ProtoOutputStream::end(long long token) +{ + if (token != mExpectedObjectToken) { + ALOGE("Unexpected token: 0x%llx, should be 0x%llx", token, mExpectedObjectToken); + return; + } + + int depth = getDepthFromToken(token); + if (depth != (mDepth & 0x01ff)) { + ALOGE("Unexpected depth: %d, should be %d", depth, mDepth); + return; + } + mDepth--; + + int sizePos = getSizePosFromToken(token); + // number of bytes written in this start-end session. + int childRawSize = mBuffer.wp()->pos() - sizePos - 8; + + // retrieve the old token from stack. + mBuffer.ep()->rewind()->move(sizePos); + mExpectedObjectToken = mBuffer.readRawFixed64(); + + // If raw size is larger than 0, write the negative value here to indicate a compact is needed. + if (childRawSize > 0) { + mBuffer.editRawFixed32(sizePos, -childRawSize); + mBuffer.editRawFixed32(sizePos+4, -1); + } else { + // reset wp which erase the header tag of the message when its size is 0. + mBuffer.wp()->rewind()->move(sizePos - getTagSizeFromToken(token)); + } +} + +bool +ProtoOutputStream::compact() { + if (mCompact) return true; + if (mDepth != 0) { + ALOGE("Can't compact when depth(%d) is not zero. Missing calls to end.", mDepth); + return false; + } + // record the size of the original buffer. + size_t rawBufferSize = mBuffer.size(); + if (rawBufferSize == 0) return true; // nothing to do if the buffer is empty; + + // reset edit pointer and recursively compute encoded size of messages. + mBuffer.ep()->rewind(); + if (editEncodedSize(rawBufferSize) == 0) { + ALOGE("Failed to editEncodedSize."); + return false; + } + + // reset both edit pointer and write pointer, and compact recursively. + mBuffer.ep()->rewind(); + mBuffer.wp()->rewind(); + if (!compactSize(rawBufferSize)) { + ALOGE("Failed to compactSize."); + return false; + } + // copy the reset to the buffer. + if (mCopyBegin < rawBufferSize) { + mBuffer.copy(mCopyBegin, rawBufferSize - mCopyBegin); + } + + // mark true means it is not legal to write to this ProtoOutputStream anymore + mCompact = true; + return true; +} + +/** + * First compaction pass. Iterate through the data, and fill in the + * nested object sizes so the next pass can compact them. + */ +size_t +ProtoOutputStream::editEncodedSize(size_t rawSize) +{ + size_t objectStart = mBuffer.ep()->pos(); + size_t objectEnd = objectStart + rawSize; + size_t encodedSize = 0; + int childRawSize, childEncodedSize; + size_t childEncodedSizePos; + + while (mBuffer.ep()->pos() < objectEnd) { + uint32_t tag = (uint32_t)mBuffer.readRawVarint(); + encodedSize += get_varint_size(tag); + switch (read_wire_type(tag)) { + case WIRE_TYPE_VARINT: + do { + encodedSize++; + } while ((mBuffer.readRawByte() & 0x80) != 0); + break; + case WIRE_TYPE_FIXED64: + encodedSize += 8; + mBuffer.ep()->move(8); + break; + case WIRE_TYPE_LENGTH_DELIMITED: + childRawSize = (int)mBuffer.readRawFixed32(); + childEncodedSizePos = mBuffer.ep()->pos(); + childEncodedSize = (int)mBuffer.readRawFixed32(); + if (childRawSize >= 0 && childRawSize == childEncodedSize) { + mBuffer.ep()->move(childRawSize); + } else if (childRawSize < 0 && childEncodedSize == -1){ + childEncodedSize = editEncodedSize(-childRawSize); + mBuffer.editRawFixed32(childEncodedSizePos, childEncodedSize); + } else { + ALOGE("Bad raw or encoded values: raw=%d, encoded=%d at %zu", + childRawSize, childEncodedSize, childEncodedSizePos); + return 0; + } + encodedSize += get_varint_size(childEncodedSize) + childEncodedSize; + break; + case WIRE_TYPE_FIXED32: + encodedSize += 4; + mBuffer.ep()->move(4); + break; + default: + ALOGE("Unexpected wire type %d in editEncodedSize at [%zu, %zu]", + read_wire_type(tag), objectStart, objectEnd); + return 0; + } + } + return encodedSize; +} + +/** + * Second compaction pass. Iterate through the data, and copy the data + * forward in the buffer, converting the pairs of uint32s into a single + * unsigned varint of the size. + */ +bool +ProtoOutputStream::compactSize(size_t rawSize) +{ + size_t objectStart = mBuffer.ep()->pos(); + size_t objectEnd = objectStart + rawSize; + int childRawSize, childEncodedSize; + + while (mBuffer.ep()->pos() < objectEnd) { + uint32_t tag = (uint32_t)mBuffer.readRawVarint(); + switch (read_wire_type(tag)) { + case WIRE_TYPE_VARINT: + while ((mBuffer.readRawByte() & 0x80) != 0) {} + break; + case WIRE_TYPE_FIXED64: + mBuffer.ep()->move(8); + break; + case WIRE_TYPE_LENGTH_DELIMITED: + mBuffer.copy(mCopyBegin, mBuffer.ep()->pos() - mCopyBegin); + + childRawSize = (int)mBuffer.readRawFixed32(); + childEncodedSize = (int)mBuffer.readRawFixed32(); + mCopyBegin = mBuffer.ep()->pos(); + + // write encoded size to buffer. + mBuffer.writeRawVarint32(childEncodedSize); + if (childRawSize >= 0 && childRawSize == childEncodedSize) { + mBuffer.ep()->move(childEncodedSize); + } else if (childRawSize < 0){ + if (!compactSize(-childRawSize)) return false; + } else { + ALOGE("Bad raw or encoded values: raw=%d, encoded=%d", + childRawSize, childEncodedSize); + return false; + } + break; + case WIRE_TYPE_FIXED32: + mBuffer.ep()->move(4); + break; + default: + ALOGE("Unexpected wire type %d in compactSize at [%zu, %zu]", + read_wire_type(tag), objectStart, objectEnd); + return false; + } + } + return true; +} + +static bool write_all(int fd, uint8_t const* buf, size_t size) +{ + while (size > 0) { + ssize_t amt = ::write(fd, buf, size); + if (amt < 0) { + return false; + } + size -= amt; + buf += amt; + } + return true; +} + +bool +ProtoOutputStream::flush() +{ + if (mFd < 0) return false; + if (!compact()) return false; + + EncodedBuffer::iterator it = mBuffer.begin(); + while (it.readBuffer() != NULL) { + if (!write_all(mFd, it.readBuffer(), it.currentToRead())) return false; + it.rp()->move(it.currentToRead()); + } + return true; +} + + +// ========================================================================= +// Private functions + +/** + * bit_cast + */ +template <class From, class To> +inline To bit_cast(From const &from) { + To to; + memcpy(&to, &from, sizeof(to)); + return to; +} + +inline void +ProtoOutputStream::writeDoubleImpl(uint32_t id, double val) +{ + if (val == 0.0) return; + mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); + mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val)); +} + +inline void +ProtoOutputStream::writeFloatImpl(uint32_t id, float val) +{ + if (val == 0.0) return; + mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); + mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val)); +} + +inline void +ProtoOutputStream::writeInt64Impl(uint32_t id, long long val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint64((uint64_t)val); +} + +inline void +ProtoOutputStream::writeInt32Impl(uint32_t id, int val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32((uint32_t)val); +} + +inline void +ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint64(val); +} + +inline void +ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32(val); +} + +inline void +ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); + mBuffer.writeRawFixed64(val); +} + +inline void +ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); + mBuffer.writeRawFixed32(val); +} + +inline void +ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_FIXED64); + mBuffer.writeRawFixed64((uint64_t)val); +} + +inline void +ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_FIXED32); + mBuffer.writeRawFixed32((uint32_t)val); +} + +inline void +ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint64((val << 1) ^ (val >> 63)); +} + +inline void +ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val) +{ + if (val == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32((val << 1) ^ (val >> 31)); +} + +inline void +ProtoOutputStream::writeEnumImpl(uint32_t id, int val) +{ + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32((uint32_t) val); +} + +inline void +ProtoOutputStream::writeBoolImpl(uint32_t id, bool val) +{ + if (!val) return; + mBuffer.writeHeader(id, WIRE_TYPE_VARINT); + mBuffer.writeRawVarint32(val ? 1 : 0); +} + +inline void +ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size) +{ + if (val == NULL || size == 0) return; + mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED); + mBuffer.writeRawFixed32(size); + mBuffer.writeRawFixed32(size); + for (size_t i=0; i<size; i++) { + mBuffer.writeRawByte((uint8_t)val[i]); + } +} + +} // util +} // android + diff --git a/libs/protoutil/src/protobuf.cpp b/libs/protoutil/src/protobuf.cpp index ec5325c57bd1..1c7eef922895 100644 --- a/libs/protoutil/src/protobuf.cpp +++ b/libs/protoutil/src/protobuf.cpp @@ -22,17 +22,28 @@ namespace util { uint8_t read_wire_type(uint32_t varint) { - return (uint8_t) (varint & 0x07); + return (uint8_t) (varint & WIRE_TYPE_MASK); } uint32_t read_field_id(uint32_t varint) { - return varint >> 3; + return varint >> FIELD_ID_SHIFT; +} + +size_t +get_varint_size(uint64_t varint) +{ + size_t size = 1; + while ((varint & ~0x7F)) { + size++; + varint >>= 7; + } + return size; } uint8_t* -write_raw_varint(uint8_t* buf, uint32_t val) +write_raw_varint(uint8_t* buf, uint64_t val) { uint8_t* p = buf; while (true) { @@ -49,7 +60,7 @@ write_raw_varint(uint8_t* buf, uint32_t val) uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size) { - buf = write_raw_varint(buf, (fieldId << 3) | 2); + buf = write_raw_varint(buf, (fieldId << FIELD_ID_SHIFT) | WIRE_TYPE_LENGTH_DELIMITED); buf = write_raw_varint(buf, size); return buf; } 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/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index d456950037d4..0704e3545b62 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -12,6 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. +cc_library_shared { + name: "libjnigraphics", + + cflags: [ + "-Wall", + "-Werror", + "-Wunused", + "-Wunreachable-code", + ], + + // our source files + // + srcs: ["bitmap.cpp"], + + shared_libs: [ + "libandroid_runtime", + ], + + arch: { + arm: { + // TODO: This is to work around b/24465209. Remove after root cause is fixed + ldflags: ["-Wl,--hash-style=both"], + }, + }, +} + // The headers module is in frameworks/native/Android.bp. ndk_library { name: "libjnigraphics", diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk deleted file mode 100644 index 7a40e62d4527..000000000000 --- a/native/graphics/jni/Android.mk +++ /dev/null @@ -1,37 +0,0 @@ -BASE_PATH := $(call my-dir) -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -# setup for skia optimizations -# -ifneq ($(ARCH_ARM_HAVE_VFP),true) - LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT -endif - -ifeq ($(ARCH_ARM_HAVE_NEON),true) - LOCAL_CFLAGS += -D__ARM_HAVE_NEON -endif - -# our source files -# -LOCAL_SRC_FILES:= \ - bitmap.cpp - -LOCAL_SHARED_LIBRARIES := \ - libandroid_runtime \ - libui \ - libandroidfw - -LOCAL_C_INCLUDES += \ - frameworks/base/core/jni/android/graphics - -LOCAL_MODULE:= libjnigraphics - -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code - -# TODO: This is to work around b/24465209. Remove after root cause is fixed -LOCAL_LDFLAGS_arm := -Wl,--hash-style=both - -include $(BUILD_SHARED_LIBRARY) - diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp index bf5cabbe4bc2..ff14832a2f0f 100644 --- a/native/graphics/jni/bitmap.cpp +++ b/native/graphics/jni/bitmap.cpp @@ -15,7 +15,7 @@ */ #include <android/bitmap.h> -#include <Bitmap.h> +#include <android/graphics/Bitmap.h> int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info) { @@ -56,4 +56,3 @@ int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) { } return ANDROID_BITMAP_RESULT_SUCCESS; } - 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 fbfa7250083b..3d083b124df2 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -289,7 +289,8 @@ <string name="launch_defaults_some">Some defaults set</string> <!-- Launch defaults preference summary with none set [CHAR LIMIT=40] --> <string name="launch_defaults_none">No defaults set</string> - + <!-- DO NOT TRANSLATE Empty summary for dynamic preferences --> + <string name="summary_empty" translatable="false"></string> <!-- Text-To-Speech (TTS) settings --><skip /> <!-- Name of the TTS package as listed by the package manager. --> <string name="tts_settings">Text-to-speech settings</string> @@ -982,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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 87971cb41f96..7b8d0db9739e 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -273,6 +273,8 @@ </intent-filter> <meta-data android:name="com.android.settings.category" android:value="com.android.settings.category.ia.system" /> + <meta-data android:name="com.android.settings.summary" + android:resource="@string/summary_empty"/> </activity> <activity-alias android:name=".DemoMode" 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/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index f9f6bf7dbcab..b32be736533b 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -1328,7 +1328,7 @@ nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong type, const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - ptr, bitmap.getSize(), usage); + ptr, bitmap.computeByteSize(), usage); return id; } @@ -1356,7 +1356,7 @@ nAllocationCubeCreateFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong ty const void* ptr = bitmap.getPixels(); jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con, (RsType)type, (RsAllocationMipmapControl)mip, - ptr, bitmap.getSize(), usage); + ptr, bitmap.computeByteSize(), usage); return id; } @@ -1371,7 +1371,7 @@ nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, j const void* ptr = bitmap.getPixels(); rsAllocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0, 0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, - w, h, ptr, bitmap.getSize(), 0); + w, h, ptr, bitmap.computeByteSize(), 0); } static void @@ -1381,7 +1381,7 @@ nAllocationCopyToBitmap(JNIEnv *_env, jobject _this, jlong con, jlong alloc, job GraphicsJNI::getSkBitmap(_env, jbitmap, &bitmap); void* ptr = bitmap.getPixels(); - rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.getSize()); + rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.computeByteSize()); bitmap.notifyPixelsChanged(); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 862070adbeee..880f236cbe76 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -216,9 +216,12 @@ final class AutofillManagerServiceImpl { serviceComponent = ComponentName.unflattenFromString(componentName); serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent, 0, mUserId); + if (serviceInfo == null) { + Slog.e(TAG, "Bad AutofillService name: " + componentName); + } } catch (RuntimeException | RemoteException e) { - Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e); - return; + Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e); + serviceInfo = null; } } try { @@ -228,21 +231,24 @@ final class AutofillManagerServiceImpl { if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo); } else { mInfo = null; - if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId); - } - final boolean isEnabled = isEnabled(); - if (wasEnabled != isEnabled) { - if (!isEnabled) { - final int sessionCount = mSessions.size(); - for (int i = sessionCount - 1; i >= 0; i--) { - final Session session = mSessions.valueAt(i); - session.removeSelfLocked(); - } + if (sDebug) { + Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")"); } - sendStateToClients(false); } } catch (Exception e) { - Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e); + Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e); + mInfo = null; + } + final boolean isEnabled = isEnabled(); + if (wasEnabled != isEnabled) { + if (!isEnabled) { + final int sessionCount = mSessions.size(); + for (int i = sessionCount - 1; i >= 0; i--) { + final Session session = mSessions.valueAt(i); + session.removeSelfLocked(); + } + } + sendStateToClients(false); } } diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java index 046eb761d1c0..8b79b9ddfef2 100644 --- a/services/core/java/com/android/server/VibratorService.java +++ b/services/core/java/com/android/server/VibratorService.java @@ -373,12 +373,24 @@ public class VibratorService extends IVibratorService.Stub if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming()) && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) { if (DEBUG) { - Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration"); + Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration"); } return; } } + // If the current vibration is repeating and the incoming one is non-repeating, then ignore + // the non-repeating vibration. This is so that we don't cancel vibrations that are meant + // to grab the attention of the user, like ringtones and alarms, in favor of one-shot + // vibrations that are likely quite short. + if (!isRepeatingVibration(effect) + && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.mEffect)) { + if (DEBUG) { + Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration"); + } + return; + } + Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg); // Only link against waveforms since they potentially don't have a finish if @@ -404,6 +416,16 @@ public class VibratorService extends IVibratorService.Stub } } + private static boolean isRepeatingVibration(VibrationEffect effect) { + if (effect instanceof VibrationEffect.Waveform) { + final VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect; + if (waveform.getRepeatIndex() >= 0) { + return true; + } + } + return false; + } + private void addToPreviousVibrationsLocked(Vibration vib) { if (mPreviousVibrations.size() > mPreviousVibrationsLimit) { mPreviousVibrations.removeFirst(); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e6fe620485da..26401a7bbf9f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -199,7 +199,6 @@ import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityManager.StackId; import android.app.ActivityManager.StackInfo; import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityManagerInternal; @@ -753,6 +752,8 @@ public class ActivityManagerService extends IActivityManager.Stub public boolean canShowErrorDialogs() { return mShowDialogs && !mSleeping && !mShuttingDown && !mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY) + && !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, + mUserController.getCurrentUserId()) && !(UserManager.isDeviceInDemoMode(mContext) && mUserController.getCurrentUser().isDemo()); } @@ -12938,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/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index b9a2d184aade..b102dde30a94 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -205,9 +205,8 @@ public class MediaSessionService extends SystemService implements Monitor { } private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { - List<MediaSessionRecord> records; + List<MediaSessionRecord> records = new ArrayList<>(); if (userId == UserHandle.USER_ALL) { - records = new ArrayList<>(); int size = mUserRecords.size(); for (int i = 0; i < size; i++) { records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId)); @@ -216,9 +215,9 @@ public class MediaSessionService extends SystemService implements Monitor { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { Log.w(TAG, "getSessions failed. Unknown user " + userId); - return new ArrayList<>(); + return records; } - records = user.mPriorityStack.getActiveSessions(userId); + records.addAll(user.mPriorityStack.getActiveSessions(userId)); } // Return global priority session at the first whenever it's asked. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7d1a6470266f..d7329dbf0e4c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -398,7 +398,7 @@ public class PackageManagerService extends IPackageManager.Stub static final boolean DEBUG_DOMAIN_VERIFICATION = false; private static final boolean DEBUG_BACKUP = false; private static final boolean DEBUG_INSTALL = false; - private static final boolean DEBUG_REMOVE = false; + public static final boolean DEBUG_REMOVE = false; private static final boolean DEBUG_BROADCASTS = false; private static final boolean DEBUG_SHOW_INFO = false; private static final boolean DEBUG_PACKAGE_INFO = false; @@ -406,7 +406,7 @@ public class PackageManagerService extends IPackageManager.Stub public static final boolean DEBUG_PACKAGE_SCANNING = false; private static final boolean DEBUG_VERIFY = false; private static final boolean DEBUG_FILTERS = false; - private static final boolean DEBUG_PERMISSIONS = false; + public static final boolean DEBUG_PERMISSIONS = false; private static final boolean DEBUG_SHARED_LIBRARIES = false; private static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE; @@ -954,9 +954,6 @@ public class PackageManagerService extends IPackageManager.Stub final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<PackageVerificationState>(); - /** Set of packages associated with each app op permission. */ - final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>(); - final PackageInstallerService mInstallerService; private final PackageDexOptimizer mPackageDexOptimizer; @@ -2881,7 +2878,7 @@ public class PackageManagerService extends IPackageManager.Stub + mSdkVersion + "; regranting permissions for internal storage"); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } - updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags); + updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags); ver.sdkVersion = mSdkVersion; // If this is the first boot or an update from pre-M, and it is a normal @@ -3368,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() { @@ -5182,7 +5181,7 @@ public class PackageManagerService extends IPackageManager.Stub final PermissionsState permissionsState = settingBase.getPermissionsState(); if (permissionsState.hasPermission(permName, userId)) { if (isUidInstantApp) { - if (mPermissionManager.isPermissionInstant(permName)) { + if (mSettings.mPermissions.isPermissionInstant(permName)) { return PackageManager.PERMISSION_GRANTED; } } else { @@ -5251,8 +5250,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - boolean addPermission(PermissionInfo info, final boolean async) { - return mPermissionManager.addPermission( + private boolean addDynamicPermission(PermissionInfo info, final boolean async) { + return mPermissionManager.addDynamicPermission( info, async, getCallingUid(), new PermissionCallback() { @Override public void onPermissionChanged() { @@ -5268,20 +5267,20 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean addPermission(PermissionInfo info) { synchronized (mPackages) { - return addPermission(info, false); + return addDynamicPermission(info, false); } } @Override public boolean addPermissionAsync(PermissionInfo info) { synchronized (mPackages) { - return addPermission(info, true); + return addDynamicPermission(info, true); } } @Override public void removePermission(String permName) { - mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback); + mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback); } @Override @@ -5942,17 +5941,8 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public String[] getAppOpPermissionPackages(String permissionName) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { - return null; - } - synchronized (mPackages) { - ArraySet<String> pkgs = mAppOpPermissionPackages.get(permissionName); - if (pkgs == null) { - return null; - } - return pkgs.toArray(new String[pkgs.size()]); - } + public String[] getAppOpPermissionPackages(String permName) { + return mPermissionManager.getAppOpPermissionPackages(permName); } @Override @@ -10346,7 +10336,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.i(TAG, "Adopting permissions from " + origName + " to " + pkg.packageName); // SIDE EFFECTS; updates permissions system state; move elsewhere - mSettings.transferPermissionsLPw(origName, pkg.packageName); + mSettings.mPermissions.transferPermissions(origName, pkg.packageName); } } } @@ -11194,54 +11184,13 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r); } - N = pkg.permissions.size(); - r = null; - for (i=0; i<N; i++) { - PackageParser.Permission p = pkg.permissions.get(i); - - // Dont allow ephemeral apps to define new permissions. - if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { - Slog.w(TAG, "Permission " + p.info.name + " from package " - + p.info.packageName - + " ignored: instant apps cannot define new permissions."); - continue; - } - // Assume by default that we did not install this permission into the system. - p.info.flags &= ~PermissionInfo.FLAG_INSTALLED; - - // Now that permission groups have a special meaning, we ignore permission - // groups for legacy apps to prevent unexpected behavior. In particular, - // permissions for one app being granted to someone just because they happen - // to be in a group defined by another app (before this had no implications). - if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) { - p.group = mPermissionGroups.get(p.info.group); - // Warn for a permission in an unknown group. - if (DEBUG_PERMISSIONS && p.info.group != null && p.group == null) { - Slog.i(TAG, "Permission " + p.info.name + " from package " - + p.info.packageName + " in an unknown group " + p.info.group); - } - } - - // TODO Move to PermissionManager once mPermissionTrees moves there. -// p.tree ? mSettings.mPermissionTrees -// : mSettings.mPermissions; -// final BasePermission bp = BasePermission.createOrUpdate( -// permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty); -// permissionMap.put(p.info.name, bp); - if (p.tree) { - final ArrayMap<String, BasePermission> permissionMap = - mSettings.mPermissionTrees; - final BasePermission bp = BasePermission.createOrUpdate( - permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, - chatty); - permissionMap.put(p.info.name, bp); - } else { - final BasePermission bp = BasePermission.createOrUpdate( - (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name), - p, pkg, mSettings.mPermissionTrees, chatty); - mPermissionManager.putPermissionTEMP(p.info.name, bp); - } + // Dont allow ephemeral apps to define new permissions. + if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { + Slog.w(TAG, "Permissions from package " + pkg.packageName + + " ignored: instant apps cannot define new permissions."); + } else { + mPermissionManager.addAllPermissions(pkg, chatty); } N = pkg.instrumentation.size(); @@ -11967,53 +11916,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r); } - N = pkg.permissions.size(); - r = null; - for (i=0; i<N; i++) { - PackageParser.Permission p = pkg.permissions.get(i); - BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name); - if (bp == null) { - bp = mSettings.mPermissionTrees.get(p.info.name); - } - if (bp != null && bp.isPermission(p)) { - bp.setPermission(null); - if (DEBUG_REMOVE && chatty) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append(p.info.name); - } - } - if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) { - ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(p.info.name); - if (appOpPkgs != null) { - appOpPkgs.remove(pkg.packageName); - } - } - } - if (r != null) { - if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); - } - - N = pkg.requestedPermissions.size(); - r = null; - for (i=0; i<N; i++) { - String perm = pkg.requestedPermissions.get(i); - if (mPermissionManager.isPermissionAppOp(perm)) { - ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm); - if (appOpPkgs != null) { - appOpPkgs.remove(pkg.packageName); - if (appOpPkgs.isEmpty()) { - mAppOpPermissionPackages.remove(perm); - } - } - } - } - if (r != null) { - if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); - } + mPermissionManager.removeAllPermissions(pkg, chatty); N = pkg.instrumentation.size(); r = null; @@ -12074,18 +11977,9 @@ public class PackageManagerService extends IPackageManager.Stub } } - private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) { - for (int i=pkgInfo.permissions.size()-1; i>=0; i--) { - if (pkgInfo.permissions.get(i).info.name.equals(perm)) { - return true; - } - } - return false; - } - - static final int UPDATE_PERMISSIONS_ALL = 1<<0; - static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1; - static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2; + public static final int UPDATE_PERMISSIONS_ALL = 1<<0; + public static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1; + public static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2; private void updatePermissionsLPw(PackageParser.Package pkg, int flags) { // Update the parent permissions @@ -12101,10 +11995,10 @@ public class PackageManagerService extends IPackageManager.Stub private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, int flags) { final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null; - updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags); + updatePermissionsLocked(changingPkg, pkgInfo, volumeUuid, flags); } - private void updatePermissionsLPw(String changingPkg, + private void updatePermissionsLocked(String changingPkg, PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) { // TODO: Most of the methods exposing BasePermission internals [source package name, // etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't @@ -12116,55 +12010,11 @@ public class PackageManagerService extends IPackageManager.Stub // normal permissions. Today, we need two separate loops because these BasePermission // objects are stored separately. // Make sure there are no dangling permission trees. - Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); - while (it.hasNext()) { - final BasePermission bp = it.next(); - if (bp.getSourcePackageSetting() == null) { - // We may not yet have parsed the package, so just see if - // we still know about its settings. - bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName())); - } - if (bp.getSourcePackageSetting() == null) { - Slog.w(TAG, "Removing dangling permission tree: " + bp.getName() - + " from package " + bp.getSourcePackageName()); - it.remove(); - } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) { - if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) { - Slog.i(TAG, "Removing old permission tree: " + bp.getName() - + " from package " + bp.getSourcePackageName()); - flags |= UPDATE_PERMISSIONS_ALL; - it.remove(); - } - } - } + flags = mPermissionManager.updatePermissionTrees(changingPkg, pkgInfo, flags); // Make sure all dynamic permissions have been assigned to a package, // and make sure there are no dangling permissions. - final Iterator<BasePermission> permissionIter = - mPermissionManager.getPermissionIteratorTEMP(); - while (permissionIter.hasNext()) { - final BasePermission bp = permissionIter.next(); - if (bp.isDynamic()) { - bp.updateDynamicPermission(mSettings.mPermissionTrees); - } - if (bp.getSourcePackageSetting() == null) { - // We may not yet have parsed the package, so just see if - // we still know about its settings. - bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName())); - } - if (bp.getSourcePackageSetting() == null) { - Slog.w(TAG, "Removing dangling permission: " + bp.getName() - + " from package " + bp.getSourcePackageName()); - permissionIter.remove(); - } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) { - if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) { - Slog.i(TAG, "Removing old permission: " + bp.getName() - + " from package " + bp.getSourcePackageName()); - flags |= UPDATE_PERMISSIONS_ALL; - permissionIter.remove(); - } - } - } + flags = mPermissionManager.updatePermissions(changingPkg, pkgInfo, flags); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions"); // Now update the permissions for all packages, in particular @@ -12286,12 +12136,7 @@ public class PackageManagerService extends IPackageManager.Stub // Keep track of app op permissions. if (bp.isAppOp()) { - ArraySet<String> pkgs = mAppOpPermissionPackages.get(perm); - if (pkgs == null) { - pkgs = new ArraySet<>(); - mAppOpPermissionPackages.put(perm, pkgs); - } - pkgs.add(pkg.packageName); + mSettings.addAppOpPackage(perm, pkg.packageName); } if (bp.isNormal()) { @@ -21238,7 +21083,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); // permissions, ensure permissions are updated. Beware of dragons if you // try optimizing this. synchronized (mPackages) { - updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, + updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL, UPDATE_PERMISSIONS_ALL); } @@ -21289,9 +21134,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL); if (mPrivappPermissionsViolations != null) { - Slog.wtf(TAG,"Signature|privileged permissions not in " + throw new IllegalStateException("Signature|privileged permissions not in " + "privapp-permissions whitelist: " + mPrivappPermissionsViolations); - mPrivappPermissionsViolations = null; } } @@ -21784,22 +21628,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) { mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState); - if (packageName == null && permissionNames == null) { - for (int iperm=0; iperm<mAppOpPermissionPackages.size(); iperm++) { - if (iperm == 0) { - if (dumpState.onTitlePrinted()) - pw.println(); - pw.println("AppOp Permissions:"); - } - pw.print(" AppOp Permission "); - pw.print(mAppOpPermissionPackages.keyAt(iperm)); - pw.println(":"); - ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm); - for (int ipkg=0; ipkg<pkgs.size(); ipkg++) { - pw.print(" "); pw.println(pkgs.valueAt(ipkg)); - } - } - } } if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { @@ -22303,7 +22131,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); + mSdkVersion + "; regranting permissions for " + volumeUuid); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } - updatePermissionsLPw(null, null, volumeUuid, updateFlags); + updatePermissionsLocked(null, null, volumeUuid, updateFlags); // Yay, everything is now upgraded ver.forceCurrent(); @@ -23311,15 +23139,15 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); void onNewUserCreated(final int userId) { synchronized(mPackages) { mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId); - } - // If permission review for legacy apps is required, we represent - // dagerous permissions for such apps as always granted runtime - // permissions to keep per user flag state whether review is needed. - // Hence, if a new user is added we have to propagate dangerous - // permission grants for these legacy apps. - if (mPermissionReviewRequired) { - updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL - | UPDATE_PERMISSIONS_REPLACE_ALL); + // If permission review for legacy apps is required, we represent + // dagerous permissions for such apps as always granted runtime + // permissions to keep per user flag state whether review is needed. + // Hence, if a new user is added we have to propagate dangerous + // permission grants for these legacy apps. + if (mPermissionReviewRequired) { + updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL + | UPDATE_PERMISSIONS_REPLACE_ALL); + } } } @@ -23763,12 +23591,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override - public Object enforcePermissionTreeTEMP(String permName, int callingUid) { + public PackageParser.PermissionGroup getPermissionGroupTEMP(String groupName) { synchronized (mPackages) { - return BasePermission.enforcePermissionTreeLP( - mSettings.mPermissionTrees, permName, callingUid); + return mPermissionGroups.get(groupName); } } + @Override public boolean isInstantApp(String packageName, int userId) { return PackageManagerService.this.isInstantApp(packageName, userId); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 0084411414a1..56595c947ed4 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -378,10 +378,6 @@ public final class Settings { private final ArrayMap<Long, Integer> mKeySetRefs = new ArrayMap<Long, Integer>(); - // Mapping from permission tree names to info about them. - final ArrayMap<String, BasePermission> mPermissionTrees = - new ArrayMap<String, BasePermission>(); - // Packages that have been uninstalled and still need their external // storage data deleted. final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>(); @@ -416,7 +412,7 @@ public final class Settings { public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages); /** Settings and other information about permissions */ - private final PermissionSettings mPermissions; + final PermissionSettings mPermissions; Settings(PermissionSettings permissions, Object lock) { this(Environment.getDataDirectory(), permissions, lock); @@ -622,6 +618,10 @@ public final class Settings { return null; } + void addAppOpPackage(String permName, String packageName) { + mPermissions.addAppOpPackage(permName, packageName); + } + SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) { SharedUserSetting s = mSharedUsers.get(name); if (s != null) { @@ -666,13 +666,6 @@ public final class Settings { } /** - * Transfers ownership of permissions from one package to another. - */ - void transferPermissionsLPw(String origPackageName, String newPackageName) { - mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees); - } - - /** * Creates a new {@code PackageSetting} object. * Use this method instead of the constructor to ensure a settings object is created * with the correct base. @@ -2496,9 +2489,7 @@ public final class Settings { } serializer.startTag(null, "permission-trees"); - for (BasePermission bp : mPermissionTrees.values()) { - writePermissionLPr(serializer, bp); - } + mPermissions.writePermissionTrees(serializer); serializer.endTag(null, "permission-trees"); serializer.startTag(null, "permissions"); @@ -3042,7 +3033,7 @@ public final class Settings { } else if (tagName.equals("permissions")) { mPermissions.readPermissions(parser); } else if (tagName.equals("permission-trees")) { - PermissionSettings.readPermissions(mPermissionTrees, parser); + mPermissions.readPermissionTrees(parser); } else if (tagName.equals("shared-user")) { readSharedUserLPw(parser); } else if (tagName.equals("preferred-packages")) { diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index a6b05d71c9f0..c18a71d380e9 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -96,6 +96,7 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_SMS, UserManager.DISALLOW_FUN, UserManager.DISALLOW_CREATE_WINDOWS, + UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE, UserManager.DISALLOW_OUTGOING_BEAM, UserManager.DISALLOW_WALLPAPER, @@ -156,6 +157,7 @@ public class UserRestrictionsUtils { private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet( UserManager.DISALLOW_ADJUST_VOLUME, UserManager.DISALLOW_BLUETOOTH_SHARING, + UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, UserManager.DISALLOW_RUN_IN_BACKGROUND, UserManager.DISALLOW_UNMUTE_MICROPHONE, UserManager.DISALLOW_UNMUTE_DEVICE diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 09a6e9c0fcaa..71d3202db853 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -48,6 +48,7 @@ import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -77,7 +78,7 @@ public final class BasePermission { final String name; - @PermissionType final int type; + final @PermissionType int type; String sourcePackageName; @@ -252,12 +253,12 @@ public final class BasePermission { return changed; } - public void updateDynamicPermission(Map<String, BasePermission> permissionTrees) { + public void updateDynamicPermission(Collection<BasePermission> permissionTrees) { if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" + getName() + " pkg=" + getSourcePackageName() + " info=" + pendingPermissionInfo); if (sourcePackageSetting == null && pendingPermissionInfo != null) { - final BasePermission tree = findPermissionTreeLP(permissionTrees, name); + final BasePermission tree = findPermissionTree(permissionTrees, name); if (tree != null && tree.perm != null) { sourcePackageSetting = tree.sourcePackageSetting; perm = new PackageParser.Permission(tree.perm.owner, @@ -269,8 +270,8 @@ public final class BasePermission { } } - public static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p, - @NonNull PackageParser.Package pkg, Map<String, BasePermission> permissionTrees, + static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p, + @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees, boolean chatty) { final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras; // Allow system apps to redefine non-system permissions @@ -300,7 +301,7 @@ public final class BasePermission { if (bp.perm == null) { if (bp.sourcePackageName == null || bp.sourcePackageName.equals(p.info.packageName)) { - final BasePermission tree = findPermissionTreeLP(permissionTrees, p.info.name); + final BasePermission tree = findPermissionTree(permissionTrees, p.info.name); if (tree == null || tree.sourcePackageName.equals(p.info.packageName)) { bp.sourcePackageSetting = pkgSetting; @@ -345,12 +346,12 @@ public final class BasePermission { return bp; } - public static BasePermission enforcePermissionTreeLP( - Map<String, BasePermission> permissionTrees, String permName, int callingUid) { + static BasePermission enforcePermissionTree( + Collection<BasePermission> permissionTrees, String permName, int callingUid) { if (permName != null) { - BasePermission bp = findPermissionTreeLP(permissionTrees, permName); + BasePermission bp = findPermissionTree(permissionTrees, permName); if (bp != null) { - if (bp.uid == UserHandle.getAppId(callingUid)) {//UserHandle.getAppId(Binder.getCallingUid())) { + if (bp.uid == UserHandle.getAppId(callingUid)) { return bp; } throw new SecurityException("Calling uid " + callingUid @@ -373,9 +374,9 @@ public final class BasePermission { } } - private static BasePermission findPermissionTreeLP( - Map<String, BasePermission> permissionTrees, String permName) { - for (BasePermission bp : permissionTrees.values()) { + private static BasePermission findPermissionTree( + Collection<BasePermission> permissionTrees, String permName) { + for (BasePermission bp : permissionTrees) { if (permName.startsWith(bp.name) && permName.length() > bp.name.length() && permName.charAt(bp.name.length()) == '.') { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java index 3b20b42b79c1..8aac52ae0df7 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java @@ -31,6 +31,7 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; /** * Internal interfaces to be used by other components within the system server. @@ -81,11 +82,26 @@ public abstract class PermissionManagerInternal { @NonNull int[] allUserIds); - public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async, + /** + * Add all permissions in the given package. + * <p> + * NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to + * the permission settings. + */ + public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty); + public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty); + public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async, int callingUid, @Nullable PermissionCallback callback); - public abstract void removePermission(@NonNull String permName, int callingUid, + public abstract void removeDynamicPermission(@NonNull String permName, int callingUid, @Nullable PermissionCallback callback); + public abstract int updatePermissions(@Nullable String changingPkg, + @Nullable PackageParser.Package pkgInfo, int flags); + public abstract int updatePermissionTrees(@Nullable String changingPkg, + @Nullable PackageParser.Package pkgInfo, int flags); + + public abstract @Nullable String[] getAppOpPermissionPackages(@NonNull String permName); + public abstract int getPermissionFlags(@NonNull String permName, @NonNull String packageName, int callingUid, int userId); /** @@ -98,8 +114,6 @@ public abstract class PermissionManagerInternal { */ public abstract @Nullable List<PermissionInfo> getPermissionInfoByGroup(@NonNull String group, @PermissionInfoFlags int flags, int callingUid); - public abstract boolean isPermissionAppOp(@NonNull String permName); - public abstract boolean isPermissionInstant(@NonNull String permName); /** * Updates the flags associated with a permission by replacing the flags in diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 6c031a6a32af..062aa3310c21 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -69,6 +69,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Set; /** * Manages all permissions and handles permissions related tasks. @@ -260,7 +261,7 @@ public class PermissionManagerService { // } final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); - for (BasePermission bp : mSettings.getAllPermissionsLocked()) { + for (BasePermission bp : mSettings.mPermissions.values()) { final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags); if (pi != null) { out.add(pi); @@ -305,7 +306,98 @@ public class PermissionManagerService { return protectionLevel; } - private boolean addPermission( + private void addAllPermissions(PackageParser.Package pkg, boolean chatty) { + final int N = pkg.permissions.size(); + for (int i=0; i<N; i++) { + PackageParser.Permission p = pkg.permissions.get(i); + + // Assume by default that we did not install this permission into the system. + p.info.flags &= ~PermissionInfo.FLAG_INSTALLED; + + // Now that permission groups have a special meaning, we ignore permission + // groups for legacy apps to prevent unexpected behavior. In particular, + // permissions for one app being granted to someone just because they happen + // to be in a group defined by another app (before this had no implications). + if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) { + p.group = mPackageManagerInt.getPermissionGroupTEMP(p.info.group); + // Warn for a permission in an unknown group. + if (PackageManagerService.DEBUG_PERMISSIONS + && p.info.group != null && p.group == null) { + Slog.i(TAG, "Permission " + p.info.name + " from package " + + p.info.packageName + " in an unknown group " + p.info.group); + } + } + + synchronized (PermissionManagerService.this.mLock) { + if (p.tree) { + final BasePermission bp = BasePermission.createOrUpdate( + mSettings.getPermissionTreeLocked(p.info.name), p, pkg, + mSettings.getAllPermissionTreesLocked(), chatty); + mSettings.putPermissionTreeLocked(p.info.name, bp); + } else { + final BasePermission bp = BasePermission.createOrUpdate( + mSettings.getPermissionLocked(p.info.name), + p, pkg, mSettings.getAllPermissionTreesLocked(), chatty); + mSettings.putPermissionLocked(p.info.name, bp); + } + } + } + } + + private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) { + synchronized (mLock) { + int N = pkg.permissions.size(); + StringBuilder r = null; + for (int i=0; i<N; i++) { + PackageParser.Permission p = pkg.permissions.get(i); + BasePermission bp = (BasePermission) mSettings.mPermissions.get(p.info.name); + if (bp == null) { + bp = mSettings.mPermissionTrees.get(p.info.name); + } + if (bp != null && bp.isPermission(p)) { + bp.setPermission(null); + if (PackageManagerService.DEBUG_REMOVE && chatty) { + if (r == null) { + r = new StringBuilder(256); + } else { + r.append(' '); + } + r.append(p.info.name); + } + } + if (p.isAppOp()) { + ArraySet<String> appOpPkgs = + mSettings.mAppOpPermissionPackages.get(p.info.name); + if (appOpPkgs != null) { + appOpPkgs.remove(pkg.packageName); + } + } + } + if (r != null) { + if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); + } + + N = pkg.requestedPermissions.size(); + r = null; + for (int i=0; i<N; i++) { + String perm = pkg.requestedPermissions.get(i); + if (mSettings.isPermissionAppOp(perm)) { + ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm); + if (appOpPkgs != null) { + appOpPkgs.remove(pkg.packageName); + if (appOpPkgs.isEmpty()) { + mSettings.mAppOpPermissionPackages.remove(perm); + } + } + } + } + if (r != null) { + if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); + } + } + } + + private boolean addDynamicPermission( PermissionInfo info, int callingUid, PermissionCallback callback) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant apps can't add permissions"); @@ -313,8 +405,7 @@ public class PermissionManagerService { if (info.labelRes == 0 && info.nonLocalizedLabel == null) { throw new SecurityException("Label must be specified in permission"); } - final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP( - info.name, callingUid); + final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid); final boolean added; final boolean changed; synchronized (mLock) { @@ -341,13 +432,12 @@ public class PermissionManagerService { return added; } - private void removePermission( + private void removeDynamicPermission( String permName, int callingUid, PermissionCallback callback) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } - final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP( - permName, callingUid); + final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid); synchronized (mLock) { final BasePermission bp = mSettings.getPermissionLocked(permName); if (bp == null) { @@ -713,7 +803,21 @@ public class PermissionManagerService { return runtimePermissionChangedUserIds; } - private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) { + private String[] getAppOpPermissionPackages(String permName) { + if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) { + return null; + } + synchronized (mLock) { + final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName); + if (pkgs == null) { + return null; + } + return pkgs.toArray(new String[pkgs.size()]); + } + } + + private int getPermissionFlags( + String permName, String packageName, int callingUid, int userId) { if (!mUserManagerInt.exists(userId)) { return 0; } @@ -741,6 +845,96 @@ public class PermissionManagerService { return permissionsState.getPermissionFlags(permName, userId); } + private int updatePermissions(String packageName, PackageParser.Package pkgInfo, int flags) { + Set<BasePermission> needsUpdate = null; + synchronized (mLock) { + final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator(); + while (it.hasNext()) { + final BasePermission bp = it.next(); + if (bp.isDynamic()) { + bp.updateDynamicPermission(mSettings.mPermissionTrees.values()); + } + if (bp.getSourcePackageSetting() != null) { + if (packageName != null && packageName.equals(bp.getSourcePackageName()) + && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) { + Slog.i(TAG, "Removing old permission tree: " + bp.getName() + + " from package " + bp.getSourcePackageName()); + flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL; + it.remove(); + } + continue; + } + if (needsUpdate == null) { + needsUpdate = new ArraySet<>(mSettings.mPermissions.size()); + } + needsUpdate.add(bp); + } + } + if (needsUpdate != null) { + for (final BasePermission bp : needsUpdate) { + final PackageParser.Package pkg = + mPackageManagerInt.getPackage(bp.getSourcePackageName()); + synchronized (mLock) { + if (pkg != null && pkg.mExtras != null) { + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (bp.getSourcePackageSetting() == null) { + bp.setSourcePackageSetting(ps); + } + continue; + } + Slog.w(TAG, "Removing dangling permission: " + bp.getName() + + " from package " + bp.getSourcePackageName()); + mSettings.removePermissionLocked(bp.getName()); + } + } + } + return flags; + } + + private int updatePermissionTrees(String packageName, PackageParser.Package pkgInfo, + int flags) { + Set<BasePermission> needsUpdate = null; + synchronized (mLock) { + final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); + while (it.hasNext()) { + final BasePermission bp = it.next(); + if (bp.getSourcePackageSetting() != null) { + if (packageName != null && packageName.equals(bp.getSourcePackageName()) + && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) { + Slog.i(TAG, "Removing old permission tree: " + bp.getName() + + " from package " + bp.getSourcePackageName()); + flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL; + it.remove(); + } + continue; + } + if (needsUpdate == null) { + needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size()); + } + needsUpdate.add(bp); + } + } + if (needsUpdate != null) { + for (final BasePermission bp : needsUpdate) { + final PackageParser.Package pkg = + mPackageManagerInt.getPackage(bp.getSourcePackageName()); + synchronized (mLock) { + if (pkg != null && pkg.mExtras != null) { + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (bp.getSourcePackageSetting() == null) { + bp.setSourcePackageSetting(ps); + } + continue; + } + Slog.w(TAG, "Removing dangling permission tree: " + bp.getName() + + " from package " + bp.getSourcePackageName()); + mSettings.removePermissionLocked(bp.getName()); + } + } + } + return flags; + } + private void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, int callingUid, int userId, PermissionCallback callback) { if (!mUserManagerInt.exists(userId)) { @@ -872,7 +1066,7 @@ public class PermissionManagerService { private int calculateCurrentPermissionFootprintLocked(BasePermission tree) { int size = 0; - for (BasePermission perm : mSettings.getAllPermissionsLocked()) { + for (BasePermission perm : mSettings.mPermissions.values()) { size += tree.calculateFootprint(perm); } return size; @@ -889,6 +1083,15 @@ public class PermissionManagerService { } } + private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) { + for (int i=pkgInfo.permissions.size()-1; i>=0; i--) { + if (pkgInfo.permissions.get(i).info.name.equals(permName)) { + return true; + } + } + return false; + } + /** * Get the first event id for the permission. * @@ -951,14 +1154,22 @@ public class PermissionManagerService { private class PermissionManagerInternalImpl extends PermissionManagerInternal { @Override - public boolean addPermission(PermissionInfo info, boolean async, int callingUid, + public void addAllPermissions(Package pkg, boolean chatty) { + PermissionManagerService.this.addAllPermissions(pkg, chatty); + } + @Override + public void removeAllPermissions(Package pkg, boolean chatty) { + PermissionManagerService.this.removeAllPermissions(pkg, chatty); + } + @Override + public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid, PermissionCallback callback) { - return PermissionManagerService.this.addPermission(info, callingUid, callback); + return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback); } @Override - public void removePermission(String permName, int callingUid, + public void removeDynamicPermission(String permName, int callingUid, PermissionCallback callback) { - PermissionManagerService.this.removePermission(permName, callingUid, callback); + PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback); } @Override public void grantRuntimePermission(String permName, String packageName, @@ -993,12 +1204,26 @@ public class PermissionManagerService { (SharedUserSetting) suSetting, allUserIds); } @Override + public String[] getAppOpPermissionPackages(String permName) { + return PermissionManagerService.this.getAppOpPermissionPackages(permName); + } + @Override public int getPermissionFlags(String permName, String packageName, int callingUid, int userId) { return PermissionManagerService.this.getPermissionFlags(permName, packageName, callingUid, userId); } @Override + public int updatePermissions(String packageName, + PackageParser.Package pkgInfo, int flags) { + return PermissionManagerService.this.updatePermissions(packageName, pkgInfo, flags); + } + @Override + public int updatePermissionTrees(String packageName, + PackageParser.Package pkgInfo, int flags) { + return PermissionManagerService.this.updatePermissionTrees(packageName, pkgInfo, flags); + } + @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, int callingUid, int userId, PermissionCallback callback) { PermissionManagerService.this.updatePermissionFlags( @@ -1038,20 +1263,6 @@ public class PermissionManagerService { return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid); } @Override - public boolean isPermissionInstant(String permName) { - synchronized (PermissionManagerService.this.mLock) { - final BasePermission bp = mSettings.getPermissionLocked(permName); - return (bp != null && bp.isInstant()); - } - } - @Override - public boolean isPermissionAppOp(String permName) { - synchronized (PermissionManagerService.this.mLock) { - final BasePermission bp = mSettings.getPermissionLocked(permName); - return (bp != null && bp.isAppOp()); - } - } - @Override public PermissionSettings getPermissionSettings() { return mSettings; } diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java index 7a2e5eccac53..7d125c9ebe87 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java +++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java @@ -24,6 +24,7 @@ import android.util.ArraySet; import android.util.Log; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.XmlUtils; import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; @@ -35,6 +36,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; +import java.util.Set; /** * Permissions and other related data. This class is not meant for @@ -49,8 +51,25 @@ public class PermissionSettings { * All of the permissions known to the system. The mapping is from permission * name to permission object. */ - private final ArrayMap<String, BasePermission> mPermissions = + @GuardedBy("mLock") + final ArrayMap<String, BasePermission> mPermissions = new ArrayMap<String, BasePermission>(); + + /** + * All permission trees known to the system. The mapping is from permission tree + * name to permission object. + */ + @GuardedBy("mLock") + final ArrayMap<String, BasePermission> mPermissionTrees = + new ArrayMap<String, BasePermission>(); + + /** + * Set of packages that request a particular app op. The mapping is from permission + * name to package names. + */ + @GuardedBy("mLock") + final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>(); + private final Object mLock; PermissionSettings(@NonNull Context context, @NonNull Object lock) { @@ -65,15 +84,23 @@ public class PermissionSettings { } } + public void addAppOpPackage(String permName, String packageName) { + ArraySet<String> pkgs = mAppOpPermissionPackages.get(permName); + if (pkgs == null) { + pkgs = new ArraySet<>(); + mAppOpPermissionPackages.put(permName, pkgs); + } + pkgs.add(packageName); + } + /** * Transfers ownership of permissions from one package to another. */ - public void transferPermissions(String origPackageName, String newPackageName, - ArrayMap<String, BasePermission> permissionTrees) { + public void transferPermissions(String origPackageName, String newPackageName) { synchronized (mLock) { for (int i=0; i<2; i++) { ArrayMap<String, BasePermission> permissions = - i == 0 ? permissionTrees : mPermissions; + i == 0 ? mPermissionTrees : mPermissions; for (BasePermission bp : permissions.values()) { bp.transfer(origPackageName, newPackageName); } @@ -94,9 +121,26 @@ public class PermissionSettings { } } + public void readPermissionTrees(XmlPullParser parser) + throws IOException, XmlPullParserException { + synchronized (mLock) { + readPermissions(mPermissionTrees, parser); + } + } + public void writePermissions(XmlSerializer serializer) throws IOException { - for (BasePermission bp : mPermissions.values()) { - bp.writeLPr(serializer); + synchronized (mLock) { + for (BasePermission bp : mPermissions.values()) { + bp.writeLPr(serializer); + } + } + } + + public void writePermissionTrees(XmlSerializer serializer) throws IOException { + synchronized (mLock) { + for (BasePermission bp : mPermissionTrees.values()) { + bp.writeLPr(serializer); + } } } @@ -128,6 +172,22 @@ public class PermissionSettings { printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames, externalStorageEnforced, printedSomething, dumpState); } + if (packageName == null && permissionNames == null) { + for (int iperm = 0; iperm<mAppOpPermissionPackages.size(); iperm++) { + if (iperm == 0) { + if (dumpState.onTitlePrinted()) + pw.println(); + pw.println("AppOp Permissions:"); + } + pw.print(" AppOp Permission "); + pw.print(mAppOpPermissionPackages.keyAt(iperm)); + pw.println(":"); + ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm); + for (int ipkg=0; ipkg<pkgs.size(); ipkg++) { + pw.print(" "); pw.println(pkgs.valueAt(ipkg)); + } + } + } } } @@ -135,15 +195,58 @@ public class PermissionSettings { return mPermissions.get(permName); } + @Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) { + return mPermissionTrees.get(permName); + } + void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) { mPermissions.put(permName, permission); } + void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) { + mPermissionTrees.put(permName, permission); + } + void removePermissionLocked(@NonNull String permName) { mPermissions.remove(permName); } - Collection<BasePermission> getAllPermissionsLocked() { + void removePermissionTreeLocked(@NonNull String permName) { + mPermissionTrees.remove(permName); + } + + @NonNull Collection<BasePermission> getAllPermissionsLocked() { return mPermissions.values(); } + + @NonNull Collection<BasePermission> getAllPermissionTreesLocked() { + return mPermissionTrees.values(); + } + + /** + * Returns the permission tree for the given permission. + * @throws SecurityException If the calling UID is not allowed to add permissions to the + * found permission tree. + */ + @Nullable BasePermission enforcePermissionTree(@NonNull String permName, int callingUid) { + synchronized (mLock) { + return BasePermission.enforcePermissionTree( + mPermissionTrees.values(), permName, callingUid); + } + } + + public boolean isPermissionInstant(String permName) { + synchronized (mLock) { + final BasePermission bp = mPermissions.get(permName); + return (bp != null && bp.isInstant()); + } + } + + boolean isPermissionAppOp(String permName) { + synchronized (mLock) { + final BasePermission bp = mPermissions.get(permName); + return (bp != null && bp.isAppOp()); + } + } + } 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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index db7817ec4955..5d871b3c7baf 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2270,7 +2270,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per // http://developer.android.com/guide/practices/screens_support.html#range - mForceDefaultOrientation = longSizeDp >= 960 && shortSizeDp >= 720 && + // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen + // so if the orientation is forced, we need to respect that no matter what. + boolean isCar = mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE); + mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar) && res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) && // For debug purposes the next line turns this feature off with: // $ adb shell setprop config.override_forced_orient true diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 05fd248ed312..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) { @@ -161,7 +249,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } - // These values must be kept in sync with cmd/statsd/StatsPuller.h. + // These values must be kept in sync with cmd/statsd/StatsPullerManager.h. // TODO: pull the constant from stats_events.proto instead private static final int PULL_CODE_KERNEL_WAKELOCKS = 20; @@ -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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 92cbd3d5537e..ff45070e599c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1640,11 +1640,7 @@ public final class SystemServer { traceEnd(); traceBeginAndSlog("MakePackageManagerServiceReady"); - try { - mPackageManagerService.systemReady(); - } catch (Throwable e) { - reportWtf("making Package Manager Service ready", e); - } + mPackageManagerService.systemReady(); traceEnd(); traceBeginAndSlog("MakeDisplayManagerServiceReady"); 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 diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java index 5a57f3221d3a..f0d60b68eb94 100644 --- a/telephony/java/android/telephony/mbms/DownloadRequest.java +++ b/telephony/java/android/telephony/mbms/DownloadRequest.java @@ -16,6 +16,7 @@ package android.telephony.mbms; +import android.annotation.NonNull; import android.annotation.SystemApi; import android.content.Intent; import android.net.Uri; @@ -26,7 +27,6 @@ import android.util.Log; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @@ -71,6 +71,19 @@ public final class DownloadRequest implements Parcelable { private String appIntent; private int version = CURRENT_VERSION; + + /** + * Builds a new DownloadRequest. + * @param sourceUri the source URI for the DownloadRequest to be built. This URI should + * never be null. + */ + public Builder(@NonNull Uri sourceUri) { + if (sourceUri == null) { + throw new IllegalArgumentException("Source URI must be non-null."); + } + source = sourceUri; + } + /** * Sets the service from which the download request to be built will download from. * @param serviceInfo @@ -92,15 +105,6 @@ public final class DownloadRequest implements Parcelable { } /** - * Sets the source URI for the download request to be built. - * @param source - */ - public Builder setSource(Uri source) { - this.source = source; - return this; - } - - /** * Set the subscription ID on which the file(s) should be downloaded. * @param subscriptionId */ @@ -316,9 +320,11 @@ public final class DownloadRequest implements Parcelable { throw new RuntimeException("Could not get sha256 hash object"); } if (version >= 1) { - // Hash the source URI, destination URI, and the app intent + // Hash the source URI and the app intent digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8)); - digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8)); + if (serializedResultIntentForApp != null) { + digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8)); + } } // Add updates for future versions here return Base64.encodeToString(digest.digest(), Base64.URL_SAFE | Base64.NO_WRAP); diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl new file mode 100644 index 000000000000..0e7289cd9c46 --- /dev/null +++ b/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.aware; + +/** + * Callback for IWifiAwareManager.getMacAddressFromPeerHandle + * + * @hide + */ +oneway interface IWifiAwareMacAddressProvider +{ + void macAddress(in Map peerIdToMacMap); +} diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl index f33424b613d8..bad5ce226efc 100644 --- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl +++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl @@ -21,6 +21,7 @@ import android.app.PendingIntent; import android.net.wifi.aware.ConfigRequest; import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; import android.net.wifi.aware.IWifiAwareEventCallback; +import android.net.wifi.aware.IWifiAwareMacAddressProvider; import android.net.wifi.aware.PublishConfig; import android.net.wifi.aware.SubscribeConfig; import android.net.wifi.aware.Characteristics; @@ -52,4 +53,7 @@ interface IWifiAwareManager void sendMessage(int clientId, int discoverySessionId, int peerId, in byte[] message, int messageId, int retryCount); void terminateSession(int clientId, int discoverySessionId); + + // internal APIs: intended to be used between System Services (restricted permissions) + void requestMacAddresses(int uid, in List peerIds, in IWifiAwareMacAddressProvider callback); } diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java index cd45c524934a..1b0aba15f962 100644 --- a/wifi/java/android/net/wifi/aware/PeerHandle.java +++ b/wifi/java/android/net/wifi/aware/PeerHandle.java @@ -32,4 +32,24 @@ public class PeerHandle { /** @hide */ public int peerId; + + /** @hide RTT_API */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (!(o instanceof PeerHandle)) { + return false; + } + + return peerId == ((PeerHandle) o).peerId; + } + + /** @hide RTT_API */ + @Override + public int hashCode() { + return peerId; + } } |