Merge "Deprecate view drawing cache"
diff --git a/Android.mk b/Android.mk
index 98e4299..6186c55 100644
--- a/Android.mk
+++ b/Android.mk
@@ -548,9 +548,10 @@
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/IWifiAwareEventCallback.aidl \
- wifi/java/android/net/wifi/aware/IWifiAwareManager.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/p2p/IWifiP2pManager.aidl \
wifi/java/android/net/wifi/rtt/IRttCallback.aidl \
wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \
@@ -1030,6 +1031,7 @@
-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 47dd257..586c385 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 @@
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 34de65d..9d11f29 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 @@
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 00b60ad..6768798 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 @@
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 356e2e0..bfdb758 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 @@
@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 a2bf33e1..ff2d57e 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.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 @@
@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 d6485cb..7b5612f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31834,6 +31834,7 @@
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 @@
}
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 3ad2371..a2863ee 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -27217,7 +27217,6 @@
}
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 @@
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 @@
}
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 7ee261e..639877f 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -275,6 +275,10 @@
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 6d94ff6..834cb36 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -32097,6 +32097,7 @@
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 @@
}
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 b16188e..e5d35b3 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -34,13 +34,6 @@
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 742f9c24..00cef43 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 @@
// 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 07a064cf..37f6ed7 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -33,18 +33,18 @@
{
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 @@
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 @@
}
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 e7a31a0..4c95007 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -42,23 +42,7 @@
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 @@
../../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 92fe844..4fbbc7a 100644
--- a/cmds/statsd/src/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/AnomalyMonitor.cpp
@@ -90,6 +90,36 @@
}
}
+// 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 d78be54..7c6e5e8 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 @@
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 0000000..1798f9d
--- /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/parse_util.h b/cmds/statsd/src/KernelWakelockPuller.h
similarity index 65%
copy from cmds/statsd/src/parse_util.h
copy to cmds/statsd/src/KernelWakelockPuller.h
index 8b82e7b..1c16f87 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/cmds/statsd/src/KernelWakelockPuller.h
@@ -13,24 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
-#include "DropboxWriter.h"
-#include "LogReader.h"
+#ifndef STATSD_KERNELWAKELOCKPULLER_H
+#define STATSD_KERNELWAKELOCKPULLER_H
-#include <log/logprint.h>
+#include <utils/String16.h>
+#include "StatsPuller.h"
namespace android {
namespace os {
namespace statsd {
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
+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 // PARSE_UTIL_H
+#endif // STATSD_KERNELWAKELOCKPULLER_H
diff --git a/cmds/statsd/src/PackageInfoListener.h b/cmds/statsd/src/PackageInfoListener.h
new file mode 100644
index 0000000..476c1d9
--- /dev/null
+++ b/cmds/statsd/src/PackageInfoListener.h
@@ -0,0 +1,38 @@
+/*
+ * 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_PACKAGE_INFO_LISTENER_H
+#define STATSD_PACKAGE_INFO_LISTENER_H
+
+#include <utils/RefBase.h>
+#include <string>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+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 117fb5e..f877ef3 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 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() {
}
-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 @@
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 88c63fa..05e441c 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 @@
class StatsLogProcessor : public LogListener {
public:
- StatsLogProcessor();
+ StatsLogProcessor(const sp<UidMap> &uidMap);
virtual ~StatsLogProcessor();
virtual void OnLogEvent(const log_msg& msg);
@@ -45,7 +46,7 @@
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.cpp b/cmds/statsd/src/StatsPuller.cpp
deleted file mode 100644
index 94e8361..0000000
--- a/cmds/statsd/src/StatsPuller.cpp
+++ /dev/null
@@ -1,60 +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 "StatsPuller"
-#define DEBUG true
-
-#include "StatsPuller.h"
-#include "StatsService.h"
-#include <android/os/IStatsCompanionService.h>
-#include <cutils/log.h>
-
-using namespace android;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-String16 StatsPuller::pull(int pullCode) {
- if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
-
- 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("");
- }
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/StatsPuller.h b/cmds/statsd/src/StatsPuller.h
index 05343b5..5e556b8 100644
--- a/cmds/statsd/src/StatsPuller.h
+++ b/cmds/statsd/src/StatsPuller.h
@@ -25,19 +25,13 @@
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/StatsPullerManager.cpp b/cmds/statsd/src/StatsPullerManager.cpp
new file mode 100644
index 0000000..f4cf1ce
--- /dev/null
+++ b/cmds/statsd/src/StatsPullerManager.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "StatsPullerManager"
+#define DEBUG true
+
+#include "StatsPullerManager.h"
+#include <android/os/IStatsCompanionService.h>
+#include <cutils/log.h>
+#include "StatsService.h"
+#include "KernelWakelockPuller.h"
+
+
+using namespace android;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const int StatsPullerManager::KERNEL_WAKELOCKS = 1;
+
+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();
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/StatsPullerManager.h b/cmds/statsd/src/StatsPullerManager.h
new file mode 100644
index 0000000..ab36df5
--- /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 ae7d66b..b496404 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -40,7 +40,8 @@
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 @@
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 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 @@
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 @@
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 a16b115..541f7e8 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 @@
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 @@
// 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 @@
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 0000000..76a7f3f
--- /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 0000000..1481010
--- /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 0000000..6188383
--- /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 0000000..38780e7
--- /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 0000000..2da8fa0
--- /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 0000000..e78c0de
--- /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 0000000..41e1707
--- /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 0000000..cb07d15
--- /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
similarity index 60%
rename from cmds/statsd/src/metrics/ConditionTracker.h
rename to cmds/statsd/src/condition/condition_util.h
index b94d5ab..a4fcea3 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 c749c3e..81e8b3d 100644
--- a/cmds/statsd/src/indexed_priority_queue.h
+++ b/cmds/statsd/src/indexed_priority_queue.h
@@ -55,6 +55,8 @@
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 @@
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 b303321..37477dc 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 @@
// 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 0000000..9f9b648
--- /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 0000000..51ee232
--- /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 0000000..4244bd5
--- /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 0000000..1c83039
--- /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 0000000..65dbe64
--- /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
similarity index 69%
rename from cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
rename to cmds/statsd/src/matchers/matcher_util.cpp
index ab7b2b1d..557c032 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 @@
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 @@
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;
- }
- }
- 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;
- }
- }
- 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;
- }
- }
- return true;
+ }
+ break;
}
- return false;
- } else {
- return matchesSimple(matcher.simple_log_entry_matcher(), event);
+ case LogicalOperation::OR: {
+ matched = false;
+ for (const int childIndex : children) {
+ if (matcherResults[childIndex] == MatchingState::kMatched) {
+ matched = true;
+ break;
+ }
+ }
+ 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;
+ }
+ }
+ break;
+ case LogicalOperation::NOR:
+ matched = true;
+ for (const int childIndex : children) {
+ if (matcherResults[childIndex] == MatchingState::kMatched) {
+ matched = false;
+ break;
+ }
+ }
+ 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 @@
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
similarity index 63%
rename from cmds/statsd/src/matchers/LogEntryMatcherManager.h
rename to cmds/statsd/src/matchers/matcher_util.h
index fc8e6a1..6d8e762 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 @@
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);
-
- static std::set<int> getTagIdsFromMatcher(const LogEntryMatcher& matcher);
-
- static bool matches(const LogEntryMatcher& matcher, const LogEventWrapper& wrapper);
-
- static bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
- const LogEventWrapper& wrapper);
+enum MatchingState {
+ kNotComputed = -1,
+ kNotMatched = 0,
+ kMatched = 1,
};
+LogEventWrapper parseLogEvent(log_msg msg);
+
+bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
+ const std::vector<MatchingState>& matcherResults);
+
+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 684ffdb..0000000
--- 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 635777f..e98999e 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 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 @@
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 @@
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 @@
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 @@
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 7502320..370cd468 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 @@
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 44a778b..b7e9656 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 cb74206..1e65f58 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 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);
- // Evaluate the conditions. Order matters, this should happen
- // before sending the event to metrics
- for (auto& condition : mConditionTracker) {
- condition->evaluateCondition(event);
+ // Since at least one of the metrics is interested in this event, we parse it now.
+ LogEventWrapper event = parseLogEvent(logMsg);
+ vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed);
+
+ for (auto& matcher : mAllLogEntryMatchers) {
+ matcher->onLogEvent(event, mAllLogEntryMatchers, matcherCache);
}
- // 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);
+ // 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]);
}
- } else {
- // TODO: we should remove any redundant matchers that the config provides.
- ALOGW("Matcher not used by any metrics.");
+ }
+ }
+ }
+
+ // 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);
+ }
}
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 77d7535..70c34db 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 @@
~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?
- // The conditions that my metrics share.
- std::vector<sp<ConditionTracker>> mConditionTracker;
+ // Hold all the log entry matchers from the config.
+ std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers;
- // the map from LogEntryMatcher names to the metrics that use this matcher.
- std::unordered_map<std::string, std::vector<std::unique_ptr<MetricProducer>>> mLogMatchers;
+ // 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;
+
+ // maps from ConditionTracker to MetricProducer
+ std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
+
+ 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 0000000..6fdd228
--- /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 0000000..5f1f295
--- /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 61421880..0000000
--- 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 2dc0cc7..6421b70 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -55,6 +55,43 @@
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 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 0000000..978b228
--- /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
similarity index 90%
rename from cmds/statsd/src/parse_util.h
rename to cmds/statsd/src/stats_util.h
index 8b82e7b..25b9bba 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 @@
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 3e4ebaf..d7702cd 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -65,7 +65,8 @@
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 @@
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 0000000..d5b6811
--- /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 0000000..f8b0fd0
--- /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 473704a..6069801 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 @@
LogEventWrapper wrapper;
wrapper.tagId = kTagIdWakelock;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestBoolMatcher) {
@@ -57,13 +58,13 @@
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 @@
wrapper.strMap[kKeyIdState] = "wakelock_name";
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
@@ -96,19 +97,19 @@
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 @@
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 @@
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 @@
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);
- 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));
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
+
+ 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);
- // 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));
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+ 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);
- // Don't set any key-value pairs.
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
+
+ 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));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
}
-TEST(LogEntryMatcherTest, TestNORMatcher) {
+TEST(LogEntryMatcherTest, TestNorMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::NOR);
+ LogicalOperation operation = LogicalOperation::NOR;
- 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_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdPackageVersion] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
-}
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-// 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);
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
- // 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);
-
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
-
- // 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 0000000..673c156
--- /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 0000000..b6f1449
--- /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 e4d4d25..74a482e 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -182,6 +182,40 @@
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 e0ac911..d988a42 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -542,9 +542,9 @@
* <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 8987bc0..23c4166 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -73,15 +73,16 @@
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 3868439..0deb2e1 100644
--- a/core/java/android/app/job/JobScheduler.java
+++ b/core/java/android/app/job/JobScheduler.java
@@ -24,7 +24,6 @@
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 @@
* 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 @@
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 9096b47..69afed2 100644
--- a/core/java/android/app/job/JobService.java
+++ b/core/java/android/app/job/JobService.java
@@ -18,16 +18,7 @@
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 @@
* </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 @@
}
/**
- * 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 @@
* {@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 be7f921..143c51d 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -467,6 +467,7 @@
/** 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 6c7c8a07..ec48ac5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6849,6 +6849,11 @@
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 b45c26c..5dd7aed 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -353,6 +353,11 @@
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 a8b8c4b..386239c 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -796,7 +796,7 @@
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 2c9fb23..4e474c8 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -683,9 +683,9 @@
*/
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 boolean hasIPv4DefaultRoute() {
for (RouteInfo r : mRoutes) {
- if (r.isIPv4Default()) {
- return true;
- }
+ if (r.isIPv4Default()) {
+ return true;
+ }
}
return false;
}
@@ -740,9 +740,9 @@
*/
public boolean hasIPv6DefaultRoute() {
for (RouteInfo r : mRoutes) {
- if (r.isIPv6Default()) {
- return true;
- }
+ if (r.isIPv6Default()) {
+ return true;
+ }
}
return false;
}
@@ -755,9 +755,9 @@
*/
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 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 9881927..450ced4 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1911,6 +1911,13 @@
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 @@
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 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 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 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 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 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 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 @@
* 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 @@
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 @@
* @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 @@
/**
* 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 @@
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 @@
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 @@
case CAMERA:
label = "camera";
break;
+ case MEMORY:
+ label = "memory";
+ break;
default:
label = "???";
}
@@ -3523,6 +3573,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- /** 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 @@
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 f8f2813..daacc4e 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -45,4 +45,20 @@
* 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 c091420..7f588ad 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -737,7 +737,9 @@
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 430a5e3..8c68871 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -574,6 +574,25 @@
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 24260c4..fba358c 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -299,7 +299,7 @@
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 @@
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 @@
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 60fff73..ac5c2e9 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -319,8 +319,6 @@
private float getJustifyWidth(int lineNum) {
Alignment paraAlign = mAlignment;
- TabStops tabStops = null;
- boolean tabStopsIsInitialized = false;
int left = 0;
int right = mWidth;
@@ -371,10 +369,6 @@
}
}
- 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 @@
float dist = Math.abs(getHorizontal(max, primary) - horiz);
if (dist <= bestdist) {
- bestdist = dist;
best = max;
}
@@ -1570,7 +1563,7 @@
// 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 @@
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 961cd8ee..4b6b6ae 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -433,7 +433,6 @@
* + 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
@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 2dbff10..20c0ed8 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -73,7 +73,7 @@
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 @@
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 @@
}
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 36fd991..dd07ddb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -681,17 +681,17 @@
}
@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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 f14394a..d27874c 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.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 ad05a51..635eed3 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1051,7 +1051,7 @@
}
// 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 @@
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 @@
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 @@
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 64e12b4..5990d7b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -174,13 +174,12 @@
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 5ea501e..90cc7bb 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -645,7 +645,7 @@
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 5c45b4b..b3bcaa0 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 <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include "jni.h"
+#include <log/log_id.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 <nativehelper/JNIHelp.h>
+#include "jni.h"
+
+#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 @@
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 @@
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 @@
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 @@
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 @@
},
{ "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 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 83ffeff..1f7277a 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -166,7 +166,7 @@
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 @@
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 @@
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 @@
{"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 9fd7c40..3b5a144 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 @@
*
* 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 @@
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 @@
*
* 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 @@
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 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 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 0000000..3a05195
--- /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 86e31d0..8d85038 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -20,6 +20,8 @@
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 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/cmds/statsd/src/parse_util.h b/core/proto/android/telephony/signalstrength.proto
similarity index 60%
copy from cmds/statsd/src/parse_util.h
copy to core/proto/android/telephony/signalstrength.proto
index 8b82e7b..ff230cb 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/core/proto/android/telephony/signalstrength.proto
@@ -13,24 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
-#include "DropboxWriter.h"
-#include "LogReader.h"
+syntax = "proto3";
-#include <log/logprint.h>
+option java_package = "android.telephony";
+option java_multiple_files = true;
-namespace android {
-namespace os {
-namespace statsd {
+package android.telephony;
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // PARSE_UTIL_H
+/**
+ * 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 6a9afbc..ff579a6 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 0000000..96af975
--- /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 a3fea29..6a404d5 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 0000000..96af975
--- /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 a3fea29..6a404d5 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 a3fea29..6a404d5 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 a3fea29..6a404d5 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 a3fea29..6a404d5 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 a3fea29..6a404d5 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 a3fea29..6a404d5 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 0000000..96af975
--- /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 0000000..96af975
--- /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 dbc9e5d..15eab1f 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -36,7 +36,8 @@
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 ac5d224..9c0543b 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 a70c604..0000000
--- 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 45e3f67..5d12f7e 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.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 @@
session.addPrinters(printers);
}
- onPrinterDiscoverySessionStartCalled();
+ onPrinterDiscoverySessionCreateCalled();
return null;
}, null, null, null, null, null, null),
null, null);
@@ -200,13 +203,18 @@
}
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 @@
@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 @@
*/
@LargeTest
@Test
- public void testGetPrintJobInfo() throws Exception {
+ public void testGetPrintJobInfo() throws Throwable {
startPrinting();
assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(),
@@ -244,6 +252,8 @@
SecurityException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -251,7 +261,7 @@
*/
@LargeTest
@Test
- public void testGetPrintJobInfos() throws Exception {
+ public void testGetPrintJobInfos() throws Throwable {
startPrinting();
List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId);
@@ -269,6 +279,8 @@
SecurityException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -276,7 +288,7 @@
*/
@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 @@
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 @@
*/
@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 @@
// Must be last as otherwise mGoodPrintJob will not be good anymore
mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
+
+ endPrinting();
}
/**
@@ -332,7 +348,7 @@
*/
@LargeTest
@Test
- public void testRestartPrintJob() throws Exception {
+ public void testRestartPrintJob() throws Throwable {
startPrinting();
mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
@@ -346,6 +362,8 @@
SecurityException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -353,7 +371,8 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@MediumTest
@Test
- public void testAddPrintServiceRecommendationsChangeListener() throws Exception {
+ @NoActivity
+ public void testAddPrintServiceRecommendationsChangeListener() throws Throwable {
final IRecommendationsChangeListener listener =
createMockIPrintServiceRecommendationsChangeListener();
@@ -479,7 +504,8 @@
*/
@MediumTest
@Test
- public void testRemovePrintServiceRecommendationsChangeListener() throws Exception {
+ @NoActivity
+ public void testRemovePrintServiceRecommendationsChangeListener() throws Throwable {
final IRecommendationsChangeListener listener =
createMockIPrintServiceRecommendationsChangeListener();
@@ -498,7 +524,8 @@
*/
@MediumTest
@Test
- public void testGetPrintServiceRecommendations() throws Exception {
+ @NoActivity
+ public void testGetPrintServiceRecommendations() throws Throwable {
assertException(() -> mIPrintManager.getPrintServiceRecommendations(mUserId),
SecurityException.class);
@@ -510,7 +537,8 @@
*/
@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 @@
*/
@LargeTest
@Test
- public void testStartPrinterDiscovery() throws Exception {
+ public void testStartPrinterDiscovery() throws Throwable {
startPrinting();
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
@@ -562,6 +590,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -569,7 +599,8 @@
*/
@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 @@
*/
@LargeTest
@Test
- public void testValidatePrinters() throws Exception {
+ public void testValidatePrinters() throws Throwable {
startPrinting();
final List<PrinterId> goodPrinters = new ArrayList<>();
@@ -617,6 +648,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -624,7 +657,7 @@
*/
@LargeTest
@Test
- public void testStartPrinterStateTracking() throws Exception {
+ public void testStartPrinterStateTracking() throws Throwable {
startPrinting();
mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
@@ -636,6 +669,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -643,7 +678,7 @@
*/
@LargeTest
@Test
- public void testGetCustomPrinterIcon() throws Exception {
+ public void testGetCustomPrinterIcon() throws Throwable {
startPrinting();
mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId);
@@ -655,6 +690,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -662,7 +699,7 @@
*/
@LargeTest
@Test
- public void testStopPrinterStateTracking() throws Exception {
+ public void testStopPrinterStateTracking() throws Throwable {
startPrinting();
mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
@@ -679,6 +716,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -686,7 +725,8 @@
*/
@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/PrintTestActivity.java b/core/tests/coretests/src/android/print/PrintTestActivity.java
deleted file mode 100644
index e9b001f..0000000
--- a/core/tests/coretests/src/android/print/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 android.print;
-
-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/core/tests/coretests/src/android/print/mockservice/MockPrintService.java b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java
deleted file mode 100644
index 9c11c22..0000000
--- 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 4e89207..0000000
--- 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 be002e2..0000000
--- 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 b58b2735..0000000
--- 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 f3a5373..0000000
--- 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 461d537..4e83221 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 @@
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 @@
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 6a3a973..902b872 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -253,12 +253,14 @@
</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 @@
<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 ed417b9..a675eaf 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -169,13 +169,13 @@
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 a534816..2a2b087 100644
--- a/libs/protoutil/Android.mk
+++ b/libs/protoutil/Android.mk
@@ -22,15 +22,15 @@
-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 cf09609..e568e4c 100644
--- a/libs/protoutil/include/android/util/EncodedBuffer.h
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -52,10 +52,10 @@
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 @@
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 @@
/**
* 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 @@
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 @@
} // 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 0000000..49ec169
--- /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 f4e8d09..ca45e26 100644
--- a/libs/protoutil/include/android/util/protobuf.h
+++ b/libs/protoutil/include/android/util/protobuf.h
@@ -24,6 +24,9 @@
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 @@
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);
/**
- * 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
+ * Get the size of a varint.
*/
-uint8_t* write_raw_varint(uint8_t* buf, uint32_t val);
+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.
+ */
+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 84dc5b6..435ae88 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 @@
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 @@
{
mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
mWp = Pointer(mChunkSize);
+ mEp = Pointer(mChunkSize);
}
EncodedBuffer::~EncodedBuffer()
@@ -137,28 +141,136 @@
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 @@
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 0000000..e9ca0dc
--- /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 ec5325c..1c7eef9 100644
--- a/libs/protoutil/src/protobuf.cpp
+++ b/libs/protoutil/src/protobuf.cpp
@@ -22,17 +22,28 @@
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 @@
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 96223ba..94c1013 100644
--- a/media/java/android/media/tv/ITvInputHardware.aidl
+++ b/media/java/android/media/tv/ITvInputHardware.aidl
@@ -40,12 +40,6 @@
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 d7a9ede..fd1f2cf 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -2590,12 +2590,9 @@
}
}
+ /** @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 7f6980d..28827e6 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 d456950..0704e35 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 7a40e62..0000000
--- 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 bf5cabb..ff14832 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 @@
}
return ANDROID_BITMAP_RESULT_SUCCESS;
}
-
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.mk b/packages/PrintSpooler/tests/outofprocess/Android.mk
index 3c02453..149be74 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.mk
+++ b/packages/PrintSpooler/tests/outofprocess/Android.mk
@@ -21,7 +21,7 @@
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 4a05f6f..307cc93 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 0000000..49eb257
--- /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 9eecf45..a6282b1 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 9a7f362..0000000
--- 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 4905a0b..0000000
--- 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 78a0cac..7ebf93d 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.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.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 @@
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 @@
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 @@
*/
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 @@
callback.onWriteFailed(e.getMessage());
}
}
- }, null);
+ }, (PrintAttributes) null);
}
@Parameterized.Parameters
@@ -303,7 +299,7 @@
} 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 @@
() -> 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 @@
} 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 @@
} 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 2ea4e7d..0000000
--- 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 3a23113..0000000
--- 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 07baa0f..0000000
--- 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 5c1260c..0000000
--- 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 be9d19b..0000000
--- 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 a828cd6..0000000
--- 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 fbfa725..3d083b1 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 7998b2e..f79be7e 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 @@
+ "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 @@
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 0000000..ba358f8
--- /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 0000000..c6552f7
--- /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 0000000..bb8404b
--- /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 0000000..ded3022
--- /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 0000000..ac61ade
--- /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 0000000..d57b64f
--- /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 56b8441..9c34763 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 @@
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 12455d8..c56e1da 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 @@
}
/**
- * 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 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 @@
}
}
- 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 55b635e..bc1a834 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -42,11 +42,12 @@
# 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_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 c3a505a..ca366ea 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_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 @@
@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 8099eb1..698e442 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 @@
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 31abecd..3af9768 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 d6bca0e..1290391 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 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 @@
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 @@
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 @@
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 94bfd8f..26d3570 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.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 @@
@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 @@
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 2f89d86..6977e09 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 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 0000000..1de7a7a
--- /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 0000000..362dbd9
--- /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 0000000..112ee64
--- /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 0000000..d0ecae3
--- /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 0000000..f68533b
--- /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 0000000..265a60b
--- /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 3e90435..dad3a28 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.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.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 @@
shadows = {TileUtilsTest.TileUtilsShadowRemoteViews.class})
public class TileUtilsTest {
- @Mock
private Context mContext;
@Mock
private PackageManager mPackageManager;
@@ -97,6 +96,7 @@
@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 @@
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 @@
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 f31d2e1..db599a7 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 @@
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 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 @@
@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 @@
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 @@
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 @@
mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
}
- private static class TestPackageManager extends DefaultPackageManager {
+ @Implements(ApplicationPackageManager.class)
+ public static class TestPackageManager extends ShadowApplicationPackageManager {
- TestPackageManager(ResourceLoader appResourceLoader) {
- super(appResourceLoader);
- }
-
- @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 a376dcd..b53cc37 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 87971cb..7b8d0db 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 1f633da..95ff13b 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 @@
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 dc626fb..851b78c 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 @@
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 09a08f0..f06cda0 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 @@
public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
+ private GlobalActions mPlugin;
private Extension<GlobalActions> mExtension;
private IStatusBarService mBarService;
@@ -41,10 +42,19 @@
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 189badf..d82f9cd 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 @@
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.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.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 @@
* 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 @@
}
}
+ /**
+ * 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 2cf230c..3563437 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.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 @@
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 @@
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 b835909..5ec3dff 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 @@
*/
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 e17a6a6..4834f38 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4665,6 +4665,9 @@
// 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 f9f6bf7d..b32be73 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1328,7 +1328,7 @@
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 @@
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 @@
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 @@
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 862070a..880f236 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 @@
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 @@
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 046eb76..8b79b9d 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -373,12 +373,24 @@
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 @@
}
}
+ 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 e6fe620..26401a7 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.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 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 @@
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 5583e86..d7cd81f 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 @@
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 c5f7528..b35ed75 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 @@
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 @@
releaseCallback(mDefaultNetworkCallback);
mDefaultNetworkCallback = null;
+ mDefaultInternetNetwork = null;
releaseCallback(mListenAllCallback);
mListenAllCallback = null;
+ mTetheringUpstreamNetwork = null;
mNetworkMap.clear();
}
@@ -207,7 +212,7 @@
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 @@
return typeStatePair.ns;
}
+ public void setCurrentUpstream(Network upstream) {
+ mTetheringUpstreamNetwork = upstream;
+ }
+
public Set<IpPrefix> getLocalPrefixes() {
return (Set<IpPrefix>) mLocalPrefixes.clone();
}
@@ -250,7 +259,7 @@
// These request*() calls can be deleted post oag/339444.
return;
}
- mCurrentDefault = network;
+ mDefaultInternetNetwork = network;
break;
case CALLBACK_MOBILE_REQUEST:
@@ -302,6 +311,13 @@
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 @@
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 @@
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 @@
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 9aabdab..9a6e609 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.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 @@
}
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 f8e5836..f930b52 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 @@
// 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 @@
}
// 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 @@
// 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 @@
}
}
- 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 @@
}
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 b9a2d18..b102dde 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 @@
}
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 @@
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 7d1a647..d7329db 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 @@
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 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 @@
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 @@
+ 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 @@
@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 @@
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 @@
}
}
- 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 @@
@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 @@
}
@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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
+ 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 @@
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 @@
}
@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 0084411..56595c9 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 @@
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 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 @@
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 @@
}
/**
- * 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 @@
}
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 @@
} 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 a6b05d7..c18a71d 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 @@
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 @@
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 09a6e9c..71d3202 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.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 @@
final String name;
- @PermissionType final int type;
+ final @PermissionType int type;
String sourcePackageName;
@@ -252,12 +253,12 @@
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 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 @@
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 @@
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 @@
}
}
- 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 3b20b42..8aac52a 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.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 @@
@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 @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 6c031a6..062aa33 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.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 @@
// }
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ 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 @@
private class PermissionManagerInternalImpl extends PermissionManagerInternal {
@Override
- public boolean addPermission(PermissionInfo info, boolean async, int callingUid,
- PermissionCallback callback) {
- return PermissionManagerService.this.addPermission(info, callingUid, callback);
+ public void addAllPermissions(Package pkg, boolean chatty) {
+ PermissionManagerService.this.addAllPermissions(pkg, chatty);
}
@Override
- public void removePermission(String permName, int callingUid,
+ public void removeAllPermissions(Package pkg, boolean chatty) {
+ PermissionManagerService.this.removeAllPermissions(pkg, chatty);
+ }
+ @Override
+ public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
PermissionCallback callback) {
- PermissionManagerService.this.removePermission(permName, callingUid, callback);
+ return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback);
+ }
+ @Override
+ public void removeDynamicPermission(String permName, int callingUid,
+ PermissionCallback callback) {
+ PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback);
}
@Override
public void grantRuntimePermission(String permName, String packageName,
@@ -993,12 +1204,26 @@
(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 @@
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 7a2e5ecc..7d125c9 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.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 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 @@
* 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 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 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 @@
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 @@
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 342ec4b..7a2e630 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 @@
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 db7817e..5d871b3 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 @@
// 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 05fd248..6a5ecfaa 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.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 @@
private final PendingIntent mAnomalyAlarmIntent;
private final PendingIntent mPollingAlarmIntent;
+ private final BroadcastReceiver mAppUpdateReceiver;
public StatsCompanionService(Context context) {
super();
@@ -63,8 +74,85 @@
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 @@
}
}
- // 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 @@
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 @@
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 0884678..b07fe98 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 @@
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 bdfbe48..c78a340 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 @@
}
@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 6117da7..c1607e9 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 @@
}
}
- @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 0e68a8f..0b65b22 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 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 @@
}
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 92cbd3d..ff45070 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1640,11 +1640,7 @@
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 0230f36..1925c39 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.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 @@
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 @@
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 @@
@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 bc07b81..e33f6c9 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.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 @@
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 @@
/* 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 @@
mStaticIpConfig = other.mStaticIpConfig;
mApfCapabilities = other.mApfCapabilities;
mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
+ mIPv6AddrGenMode = other.mIPv6AddrGenMode;
+ mNetwork = other.mNetwork;
+ mDisplayName = other.mDisplayName;
}
@Override
@@ -388,6 +404,8 @@
.add("mApfCapabilities: " + mApfCapabilities)
.add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
.add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
+ .add("mNetwork: " + mNetwork)
+ .add("mDisplayName: " + mDisplayName)
.toString();
}
@@ -1441,10 +1459,10 @@
@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 @@
}
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 @@
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 343d237..bbd3d13 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -106,6 +106,10 @@
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 1f6dda1..cc8bd69 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 @@
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 @@
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 5a57f32..f0d60b6 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 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 @@
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 @@
}
/**
- * 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 @@
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/cmds/statsd/src/parse_util.h b/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl
similarity index 64%
copy from cmds/statsd/src/parse_util.h
copy to wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl
index 8b82e7b..0e7289c 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl
@@ -13,24 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
-#include "DropboxWriter.h"
-#include "LogReader.h"
+package android.net.wifi.aware;
-#include <log/logprint.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // PARSE_UTIL_H
+/**
+ * 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 f33424b..bad5ce2 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.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 @@
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 cd45c52..1b0aba1 100644
--- a/wifi/java/android/net/wifi/aware/PeerHandle.java
+++ b/wifi/java/android/net/wifi/aware/PeerHandle.java
@@ -32,4 +32,24 @@
/** @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;
+ }
}