Merge "Introduce OEM customization XML parser"
diff --git a/api/current.txt b/api/current.txt
index 87aaaa6..7d6c939 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -7,6 +7,7 @@
public static final class Manifest.permission {
ctor public Manifest.permission();
field public static final java.lang.String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
+ field public static final java.lang.String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION";
field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
@@ -32521,6 +32522,7 @@
public class Build {
ctor public Build();
+ method public static java.util.List<android.os.Build.Partition> getPartitions();
method public static java.lang.String getRadioVersion();
method public static java.lang.String getSerial();
field public static final java.lang.String BOARD;
@@ -32549,6 +32551,14 @@
field public static final java.lang.String USER;
}
+ public static class Build.Partition {
+ ctor public Build.Partition();
+ method public java.lang.String getFingerprint();
+ method public java.lang.String getName();
+ method public long getTimeMillis();
+ field public static final java.lang.String PARTITION_NAME_SYSTEM = "system";
+ }
+
public static class Build.VERSION {
ctor public Build.VERSION();
field public static final java.lang.String BASE_OS;
@@ -42568,7 +42578,7 @@
method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
}
- public class NeighboringCellInfo implements android.os.Parcelable {
+ public deprecated class NeighboringCellInfo implements android.os.Parcelable {
ctor public deprecated NeighboringCellInfo();
ctor public deprecated NeighboringCellInfo(int, int);
ctor public NeighboringCellInfo(int, java.lang.String, int);
@@ -43029,7 +43039,6 @@
method public java.lang.String getMmsUAProfUrl();
method public java.lang.String getMmsUserAgent();
method public java.lang.String getNai();
- method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public java.lang.String getNetworkCountryIso();
method public java.lang.String getNetworkOperator();
method public java.lang.String getNetworkOperatorName();
@@ -45696,6 +45705,7 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public V get(java.lang.Object);
method public int indexOfKey(java.lang.Object);
+ method public int indexOfValue(java.lang.Object);
method public boolean isEmpty();
method public K keyAt(int);
method public java.util.Set<K> keySet();
@@ -45716,6 +45726,7 @@
ctor public ArraySet();
ctor public ArraySet(int);
ctor public ArraySet(android.util.ArraySet<E>);
+ ctor public ArraySet(java.util.Collection<? extends E>);
method public boolean add(E);
method public void addAll(android.util.ArraySet<? extends E>);
method public boolean addAll(java.util.Collection<? extends E>);
@@ -46287,6 +46298,7 @@
method public int keyAt(int);
method public void put(int, boolean);
method public void removeAt(int);
+ method public void setValueAt(int, boolean);
method public int size();
method public boolean valueAt(int);
}
@@ -46305,6 +46317,7 @@
method public int keyAt(int);
method public void put(int, int);
method public void removeAt(int);
+ method public void setValueAt(int, int);
method public int size();
method public int valueAt(int);
}
diff --git a/api/removed.txt b/api/removed.txt
index b6dabcd..f7106d2 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -545,6 +545,7 @@
}
public class TelephonyManager {
+ method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
method public deprecated android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index d3496ed..e090ed1 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -99,6 +99,7 @@
libhidlbase \
libhidltransport \
libhwbinder \
+ android.frameworks.stats@1.0 \
android.hardware.health@2.0 \
android.hardware.power@1.0 \
android.hardware.power@1.1 \
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 2ef1169..8da2d44 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -992,6 +992,60 @@
return Status::ok();
}
+hardware::Return<void> StatsService::reportSpeakerImpedance(
+ const SpeakerImpedance& speakerImpedance) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), speakerImpedance);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), hardwareFailed);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportPhysicalDropDetected(
+ const PhysicalDropDetected& physicalDropDetected) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), physicalDropDetected);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportChargeCycles(const ChargeCycles& chargeCycles) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), chargeCycles);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportBatteryHealthSnapshot(
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(),
+ batteryHealthSnapshotArgs);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportSlowIo(const SlowIo& slowIo) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), slowIo);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsService::reportBatteryCausedShutdown(
+ const BatteryCausedShutdown& batteryCausedShutdown) {
+ LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), batteryCausedShutdown);
+ mProcessor->OnLogEvent(&event);
+
+ return hardware::Void();
+}
+
void StatsService::binderDied(const wp <IBinder>& who) {
ALOGW("statscompanion service died");
StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0618927..1f1d782 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,6 +27,8 @@
#include "shell/ShellSubscriber.h"
#include "statscompanion_util.h"
+#include <android/frameworks/stats/1.0/IStats.h>
+#include <android/frameworks/stats/1.0/types.h>
#include <android/os/BnStatsManager.h>
#include <android/os/IStatsCompanionService.h>
#include <binder/IResultReceiver.h>
@@ -38,6 +40,7 @@
using namespace android;
using namespace android::base;
using namespace android::binder;
+using namespace android::frameworks::stats::V1_0;
using namespace android::os;
using namespace std;
@@ -45,7 +48,12 @@
namespace os {
namespace statsd {
-class StatsService : public BnStatsManager, public LogListener, public IBinder::DeathRecipient {
+using android::hardware::Return;
+
+class StatsService : public BnStatsManager,
+ public LogListener,
+ public IStats,
+ public IBinder::DeathRecipient {
public:
StatsService(const sp<Looper>& handlerLooper);
virtual ~StatsService();
@@ -146,6 +154,44 @@
*/
virtual Status sendAppBreadcrumbAtom(int32_t label, int32_t state) override;
+ /**
+ * Binder call to get SpeakerImpedance atom.
+ */
+ virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
+
+ /**
+ * Binder call to get HardwareFailed atom.
+ */
+ virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override;
+
+ /**
+ * Binder call to get PhysicalDropDetected atom.
+ */
+ virtual Return<void> reportPhysicalDropDetected(
+ const PhysicalDropDetected& physicalDropDetected) override;
+
+ /**
+ * Binder call to get ChargeCyclesReported atom.
+ */
+ virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
+
+ /**
+ * Binder call to get BatteryHealthSnapshot atom.
+ */
+ virtual Return<void> reportBatteryHealthSnapshot(
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override;
+
+ /**
+ * Binder call to get SlowIo atom.
+ */
+ virtual Return<void> reportSlowIo(const SlowIo& slowIo) override;
+
+ /**
+ * Binder call to get BatteryCausedShutdown atom.
+ */
+ virtual Return<void> reportBatteryCausedShutdown(
+ const BatteryCausedShutdown& batteryCausedShutdown) override;
+
/** IBinder::DeathRecipient */
virtual void binderDied(const wp<IBinder>& who) override;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 10a00ee..30d8bfc 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -138,6 +138,9 @@
FingerprintAuthenticated fingerprint_authenticated = 88;
FingerprintErrorOccurred fingerprint_error_occurred = 89;
Notification notification = 90;
+ BatteryHealthSnapshot battery_health_snapshot = 91;
+ SlowIo slow_io = 92;
+ BatteryCausedShutdown battery_caused_shutdown = 93;
}
// Pulled events will start at field 10000.
@@ -1313,6 +1316,68 @@
}
/**
+ * Log battery health snapshot.
+ *
+ * Resistance, Voltage, Open Circuit Voltage, Temperature, and Charge Level
+ * are snapshotted periodically over 24hrs.
+ */
+message BatteryHealthSnapshot {
+ enum BatterySnapshotType {
+ UNKNOWN = 0;
+ MIN_TEMP = 1; // Snapshot at min batt temp over 24hrs.
+ MAX_TEMP = 2; // Snapshot at max batt temp over 24hrs.
+ MIN_RESISTANCE = 3; // Snapshot at min batt resistance over 24hrs.
+ MAX_RESISTANCE = 4; // Snapshot at max batt resistance over 24hrs.
+ MIN_VOLTAGE = 5; // Snapshot at min batt voltage over 24hrs.
+ MAX_VOLTAGE = 6; // Snapshot at max batt voltage over 24hrs.
+ MIN_CURRENT = 7; // Snapshot at min batt current over 24hrs.
+ MAX_CURRENT = 8; // Snapshot at max batt current over 24hrs.
+ MIN_BATT_LEVEL = 9; // Snapshot at min battery level (SoC) over 24hrs.
+ MAX_BATT_LEVEL = 10; // Snapshot at max battery level (SoC) over 24hrs.
+ AVG_RESISTANCE = 11; // Snapshot at average battery resistance over 24hrs.
+ }
+ optional BatterySnapshotType type = 1;
+ // Temperature, in 1/10ths of degree C.
+ optional int32 temperature_deci_celcius = 2;
+ // Voltage Battery Voltage, in microVolts.
+ optional int32 voltage_micro_volt = 3;
+ // Current Battery current, in microAmps.
+ optional int32 current_micro_amps = 4;
+ // OpenCircuitVoltage Battery Open Circuit Voltage, in microVolts.
+ optional int32 open_circuit_micro_volt = 5;
+ // Resistance Battery Resistance, in microOhms.
+ optional int32 resistance_micro_ohm = 6;
+ // Level Battery Level, as % of full.
+ optional int32 level_percent = 7;
+}
+
+/**
+ * Log slow I/O operations on the primary storage.
+ */
+message SlowIo {
+ // Classifications of IO Operations.
+ enum IoOperation {
+ UNKNOWN = 0;
+ READ = 1;
+ WRITE = 2;
+ UNMAP = 3;
+ SYNC = 4;
+ }
+ optional IoOperation operation = 1;
+
+ // The number of slow IO operations of this type over 24 hours.
+ optional int32 count = 2;
+}
+
+/**
+ * Log battery caused shutdown with the last recorded voltage.
+ */
+message BatteryCausedShutdown {
+ // The last recorded battery voltage prior to shutdown.
+ optional int32 last_recorded_micro_volt = 1;
+}
+
+/**
* Logs the duration of a davey (jank of >=700ms) when it occurs
*
* Logged from:
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 66392f8..5fb196f 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -163,10 +163,7 @@
new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
// battery_voltage
{android::util::BATTERY_VOLTAGE,
- {{},
- {},
- 1 * NS_PER_SEC,
- new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
+ {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
// process_memory_state
{android::util::PROCESS_MEMORY_STATE,
{{4, 5, 6, 7, 8, 9},
@@ -204,17 +201,17 @@
{{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}},
// Size of specific categories of files. Eg. Music.
{android::util::CATEGORY_SIZE,
- {{},
- {},
- 1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
// Number of fingerprints registered to each user.
{android::util::NUM_FINGERPRINTS,
{{},
{},
1 * NS_PER_SEC,
new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}},
- };
+ // ProcStats.
+ {android::util::PROC_STATS,
+ {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 3afa08f..4bbcfd5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -146,6 +146,127 @@
}
}
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const SpeakerImpedance& speakerImpedance) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::SPEAKER_IMPEDANCE_REPORTED;
+
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(1)), Value(speakerImpedance.speakerLocation)));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(2)), Value(speakerImpedance.milliOhms)));
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const HardwareFailed& hardwareFailed) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::HARDWARE_FAILED;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(int32_t(hardwareFailed.hardwareType))));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(2)), Value(hardwareFailed.hardwareLocation)));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(3)), Value(int32_t(hardwareFailed.errorCode))));
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const PhysicalDropDetected& physicalDropDetected) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::PHYSICAL_DROP_DETECTED;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(int32_t(physicalDropDetected.confidencePctg))));
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(2)), Value(physicalDropDetected.accelPeak)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)),
+ Value(physicalDropDetected.freefallDuration)));
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const ChargeCycles& chargeCycles) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::CHARGE_CYCLES_REPORTED;
+
+ for (size_t i = 0; i < chargeCycles.cycleBucket.size(); i++) {
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 1)),
+ Value(chargeCycles.cycleBucket[i])));
+ }
+
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::BATTERY_HEALTH_SNAPSHOT;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(int32_t(batteryHealthSnapshotArgs.type))));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)),
+ Value(batteryHealthSnapshotArgs.temperatureDeciC)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)),
+ Value(batteryHealthSnapshotArgs.voltageMicroV)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)),
+ Value(batteryHealthSnapshotArgs.currentMicroA)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(5)),
+ Value(batteryHealthSnapshotArgs.openCircuitVoltageMicroV)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)),
+ Value(batteryHealthSnapshotArgs.resistanceMicroOhm)));
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)),
+ Value(batteryHealthSnapshotArgs.levelPercent)));
+
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const SlowIo& slowIo) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::SLOW_IO;
+
+ int pos[] = {1};
+ mValues.push_back(
+ FieldValue(Field(mTagId, getSimpleField(1)), Value(int32_t(slowIo.operation))));
+ pos[0]++;
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(slowIo.count)));
+
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
+LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const BatteryCausedShutdown& batteryCausedShutdown) {
+ mLogdTimestampNs = wallClockTimestampNs;
+ mElapsedTimestampNs = elapsedTimestampNs;
+ mTagId = android::util::BATTERY_CAUSED_SHUTDOWN;
+
+ mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)),
+ Value(batteryCausedShutdown.voltageMicroV)));
+
+ if (!mValues.empty()) {
+ mValues.back().mField.decorateLastPos(1);
+ }
+}
+
LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) {
mLogdTimestampNs = timestampNs;
mTagId = tagId;
@@ -223,8 +344,6 @@
return false;
}
-
-
bool LogEvent::writeKeyValuePairs(const std::map<int32_t, int32_t>& int_map,
const std::map<int32_t, int64_t>& long_map,
const std::map<int32_t, std::string>& string_map,
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 0a8ac2b..c7e2a8c 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,6 +18,7 @@
#include "FieldValue.h"
+#include <android/frameworks/stats/1.0/types.h>
#include <android/os/StatsLogEventWrapper.h>
#include <android/util/ProtoOutputStream.h>
#include <log/log_event_list.h>
@@ -28,6 +29,8 @@
#include <string>
#include <vector>
+using namespace android::frameworks::stats::V1_0;
+
namespace android {
namespace os {
namespace statsd {
@@ -82,6 +85,27 @@
const std::map<int32_t, std::string>& string_map,
const std::map<int32_t, float>& float_map);
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const SpeakerImpedance& speakerImpedance);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const HardwareFailed& hardwareFailed);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const PhysicalDropDetected& physicalDropDetected);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const ChargeCycles& chargeCycles);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const SlowIo& slowIo);
+
+ explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
+ const BatteryCausedShutdown& batteryCausedShutdown);
+
~LogEvent();
/**
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 9002f07..a5dac08 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -25,6 +25,7 @@
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <binder/Status.h>
+#include <hidl/HidlTransportSupport.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
@@ -56,12 +57,21 @@
ps->giveThreadPoolName();
IPCThreadState::self()->disableBackgroundScheduling(true);
+ ::android::hardware::configureRpcThreadpool(1 /*threads*/, false /*willJoin*/);
+
// Create the service
sp<StatsService> service = new StatsService(looper);
if (defaultServiceManager()->addService(String16("stats"), service) != 0) {
- ALOGE("Failed to add service");
+ ALOGE("Failed to add service as AIDL service");
return -1;
}
+
+ auto ret = service->registerAsService();
+ if (ret != ::android::OK) {
+ ALOGE("Failed to add service as HIDL service");
+ return 1; // or handle error
+ }
+
service->sayHiToStatsCompanion();
service->Startup();
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 9d9e5be..dd3402d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -781,7 +781,7 @@
if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
for (const auto& condIt : whatIt->second) {
const bool cond = dimensionKeysInCondition.find(condIt.first) !=
- dimensionKeysInCondition.end();
+ dimensionKeysInCondition.end() && condition;
handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first),
conditionKey, cond, event);
dimensionKeysInCondition.erase(condIt.first);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index f038214..75bd40f 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -81,6 +81,34 @@
} // namespace
+/*
+ The following test has the following input.
+
+{ 10000000002 10000000002 (8)9999[I], [S], job0[S], 1[I], }
+{ 10000000010 10000000010 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
+{ 10000000011 10000000011 (29)1[I], }
+{ 10000000040 10000000040 (29)2[I], }
+{ 10000000050 10000000050 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
+{ 10000000101 10000000101 (8)9999[I], [S], job0[S], 0[I], }
+{ 10000000102 10000000102 (29)1[I], }
+{ 10000000200 10000000200 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
+{ 10000000201 10000000201 (8)9999[I], [S], job2[S], 1[I], }
+{ 10000000400 10000000400 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 1[I], }
+{ 10000000401 10000000401 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 1[I], }
+{ 10000000450 10000000450 (29)2[I], }
+{ 10000000500 10000000500 (8)9999[I], [S], job2[S], 0[I], }
+{ 10000000600 10000000600 (8)8888[I], [S], job2[S], 1[I], }
+{ 10000000650 10000000650 (29)1[I], }
+{ 309999999999 309999999999 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 0[I], }
+{ 310000000100 310000000100 (29)2[I], }
+{ 310000000300 310000000300 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
+{ 310000000600 310000000600 (8)8888[I], [S], job1[S], 1[I], }
+{ 310000000640 310000000640 (29)1[I], }
+{ 310000000650 310000000650 (29)2[I], }
+{ 310000000700 310000000700 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 0[I], }
+{ 310000000850 310000000850 (8)8888[I], [S], job2[S], 0[I], }
+{ 310000000900 310000000900 (8)8888[I], [S], job1[S], 0[I], }
+*/
TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition) {
for (const bool hashStringInReport : { true, false }) {
for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : { true, false }) {
@@ -250,7 +278,7 @@
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 600);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 650);
EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
@@ -269,7 +297,7 @@
data.dimensions_in_condition(),
android::util::SYNC_STATE_CHANGED, 333, "App2");
EXPECT_EQ(data.bucket_info_size(), 2);
- EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 600);
+ EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 650);
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
@@ -331,7 +359,7 @@
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 100);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
@@ -353,7 +381,7 @@
EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs);
EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
- EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 110);
+ EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 110);
EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(),
bucketStartTimeNs + bucketSizeNs);
EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(),
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2acae1c..482ef2d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -394,7 +394,7 @@
* <td>The final call you receive before your
* activity is destroyed. This can happen either because the
* activity is finishing (someone called {@link Activity#finish} on
- * it, or because the system is temporarily destroying this
+ * it), or because the system is temporarily destroying this
* instance of the activity to save space. You can distinguish
* between these two scenarios with the {@link
* Activity#isFinishing} method.</td>
@@ -1985,7 +1985,7 @@
/**
* Perform any final cleanup before an activity is destroyed. This can
* happen either because the activity is finishing (someone called
- * {@link #finish} on it, or because the system is temporarily destroying
+ * {@link #finish} on it), or because the system is temporarily destroying
* this instance of the activity to save space. You can distinguish
* between these two scenarios with the {@link #isFinishing} method.
*
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 2718bfa..bf3d885 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -208,10 +209,11 @@
}
/**
- * Expand the settings panel and open a subPanel, pass null to just open the settings panel.
+ * Expand the settings panel and open a subPanel. If the subpanel is null or does not have a
+ * corresponding tile, the QS panel is simply expanded
*/
@UnsupportedAppUsage
- public void expandSettingsPanel(String subPanel) {
+ public void expandSettingsPanel(@Nullable String subPanel) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index e6fb5dc..096c7aa 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -28,9 +28,13 @@
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
import android.view.DisplayInfo;
+import java.io.IOException;
+
/**
* Class that contains windowing configuration/state for other objects that contain windows directly
* or indirectly. E.g. Activities, Task, Displays, ...
@@ -511,6 +515,38 @@
}
/**
+ * Read from a protocol buffer input stream.
+ * Protocol buffer message definition at {@link android.app.WindowConfigurationProto}
+ *
+ * @param proto Stream to read the WindowConfiguration object from.
+ * @param fieldId Field Id of the WindowConfiguration as defined in the parent message
+ * @hide
+ */
+ public void readFromProto(ProtoInputStream proto, long fieldId)
+ throws IOException, WireTypeMismatchException {
+ final long token = proto.start(fieldId);
+ try {
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) APP_BOUNDS:
+ mAppBounds = new Rect();
+ mAppBounds.readFromProto(proto, APP_BOUNDS);
+ break;
+ case (int) WINDOWING_MODE:
+ mWindowingMode = proto.readInt(WINDOWING_MODE);
+ break;
+ case (int) ACTIVITY_TYPE:
+ mActivityType = proto.readInt(ACTIVITY_TYPE);
+ break;
+ }
+ }
+ } finally {
+ // Let caller handle any exceptions
+ proto.end(token);
+ }
+ }
+
+ /**
* Returns true if the activities associated with this window configuration display a shadow
* around their border.
* @hide
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 9f22ad1..308b39e 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -165,6 +165,12 @@
*/
public static final int KEYGUARD_HIDDEN = 18;
+ /**
+ * Keep in sync with the greatest event type value.
+ * @hide
+ */
+ public static final int MAX_EVENT_TYPE = 18;
+
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
@@ -176,6 +182,12 @@
public @interface EventFlags {}
/**
+ * Bitwise OR all valid flag constants to create this constant.
+ * @hide
+ */
+ public static final int VALID_FLAG_BITS = FLAG_IS_PACKAGE_INSTANT_APP;
+
+ /**
* {@hide}
*/
@UnsupportedAppUsage
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 121b432..799f8e5 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -46,6 +46,7 @@
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.WindowConfiguration;
+import android.content.LocaleProto;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
import android.os.Build;
@@ -54,7 +55,9 @@
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.DisplayMetrics;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
import android.view.View;
import com.android.internal.util.XmlUtils;
@@ -67,6 +70,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
/**
@@ -1086,12 +1090,14 @@
/**
* Write to a protocol buffer output stream.
* Protocol buffer message definition at {@link android.content.ConfigurationProto}
+ * Has the option to ignore fields that don't need to be persisted to disk.
*
* @param protoOutputStream Stream to write the Configuration object to.
* @param fieldId Field Id of the Configuration as defined in the parent message
+ * @param persisted Note if this proto will be persisted to disk
* @hide
*/
- public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId, boolean persisted) {
final long token = protoOutputStream.start(fieldId);
protoOutputStream.write(FONT_SCALE, fontScale);
protoOutputStream.write(MCC, mcc);
@@ -1113,13 +1119,137 @@
protoOutputStream.write(SCREEN_HEIGHT_DP, screenHeightDp);
protoOutputStream.write(SMALLEST_SCREEN_WIDTH_DP, smallestScreenWidthDp);
protoOutputStream.write(DENSITY_DPI, densityDpi);
- if (windowConfiguration != null) {
+ // For persistence, we do not care about window configuration
+ if (!persisted && windowConfiguration != null) {
windowConfiguration.writeToProto(protoOutputStream, WINDOW_CONFIGURATION);
}
protoOutputStream.end(token);
}
/**
+ * Write to a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+ *
+ * @param protoOutputStream Stream to write the Configuration object to.
+ * @param fieldId Field Id of the Configuration as defined in the parent message
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ writeToProto(protoOutputStream, fieldId, false);
+ }
+
+ /**
+ * Read from a protocol buffer output stream.
+ * Protocol buffer message definition at {@link android.content.ConfigurationProto}
+ *
+ * @param protoInputStream Stream to read the Configuration object from.
+ * @param fieldId Field Id of the Configuration as defined in the parent message
+ * @hide
+ */
+ public void readFromProto(ProtoInputStream protoInputStream, long fieldId) throws IOException {
+ final long token = protoInputStream.start(fieldId);
+ final List<Locale> list = new ArrayList();
+ try {
+ while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (protoInputStream.getFieldNumber()) {
+ case (int) FONT_SCALE:
+ fontScale = protoInputStream.readFloat(FONT_SCALE);
+ break;
+ case (int) MCC:
+ mcc = protoInputStream.readInt(MCC);
+ break;
+ case (int) MNC:
+ mnc = protoInputStream.readInt(MNC);
+ break;
+ case (int) LOCALES:
+ // Parse the Locale here to handle all the repeated Locales
+ // The LocaleList will be created when the message is completed
+ final long localeToken = protoInputStream.start(LOCALES);
+ String language = "";
+ String country = "";
+ String variant = "";
+ try {
+ while (protoInputStream.nextField()
+ != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (protoInputStream.getFieldNumber()) {
+ case (int) LocaleProto.LANGUAGE:
+ language = protoInputStream.readString(
+ LocaleProto.LANGUAGE);
+ break;
+ case (int) LocaleProto.COUNTRY:
+ country = protoInputStream.readString(LocaleProto.COUNTRY);
+ break;
+ case (int) LocaleProto.VARIANT:
+ variant = protoInputStream.readString(LocaleProto.VARIANT);
+ break;
+ }
+ }
+ } catch (WireTypeMismatchException wtme) {
+ // rethrow for caller deal with
+ throw wtme;
+ } finally {
+ protoInputStream.end(localeToken);
+ list.add(new Locale(language, country, variant));
+ }
+ break;
+ case (int) SCREEN_LAYOUT:
+ screenLayout = protoInputStream.readInt(SCREEN_LAYOUT);
+ break;
+ case (int) COLOR_MODE:
+ colorMode = protoInputStream.readInt(COLOR_MODE);
+ break;
+ case (int) TOUCHSCREEN:
+ touchscreen = protoInputStream.readInt(TOUCHSCREEN);
+ break;
+ case (int) KEYBOARD:
+ keyboard = protoInputStream.readInt(KEYBOARD);
+ break;
+ case (int) KEYBOARD_HIDDEN:
+ keyboardHidden = protoInputStream.readInt(KEYBOARD_HIDDEN);
+ break;
+ case (int) HARD_KEYBOARD_HIDDEN:
+ hardKeyboardHidden = protoInputStream.readInt(HARD_KEYBOARD_HIDDEN);
+ break;
+ case (int) NAVIGATION:
+ navigation = protoInputStream.readInt(NAVIGATION);
+ break;
+ case (int) NAVIGATION_HIDDEN:
+ navigationHidden = protoInputStream.readInt(NAVIGATION_HIDDEN);
+ break;
+ case (int) ORIENTATION:
+ orientation = protoInputStream.readInt(ORIENTATION);
+ break;
+ case (int) UI_MODE:
+ uiMode = protoInputStream.readInt(UI_MODE);
+ break;
+ case (int) SCREEN_WIDTH_DP:
+ screenWidthDp = protoInputStream.readInt(SCREEN_WIDTH_DP);
+ break;
+ case (int) SCREEN_HEIGHT_DP:
+ screenHeightDp = protoInputStream.readInt(SCREEN_HEIGHT_DP);
+ break;
+ case (int) SMALLEST_SCREEN_WIDTH_DP:
+ smallestScreenWidthDp = protoInputStream.readInt(SMALLEST_SCREEN_WIDTH_DP);
+ break;
+ case (int) DENSITY_DPI:
+ densityDpi = protoInputStream.readInt(DENSITY_DPI);
+ break;
+ case (int) WINDOW_CONFIGURATION:
+ windowConfiguration.readFromProto(protoInputStream, WINDOW_CONFIGURATION);
+ break;
+ }
+ }
+ } finally {
+ // Let caller handle any exceptions
+ if (list.size() > 0) {
+ //Create the LocaleList from the collected Locales
+ setLocales(new LocaleList(list.toArray(new Locale[list.size()])));
+ }
+ protoInputStream.end(token);
+ }
+ }
+
+ /**
* Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
* stream.
*
diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java
index 94f2ac0..0d7b695 100644
--- a/core/java/android/hardware/GeomagneticField.java
+++ b/core/java/android/hardware/GeomagneticField.java
@@ -31,7 +31,7 @@
* Android may use a newer version of the model.
*/
public class GeomagneticField {
- // The magnetic field at a given point, in nonoteslas in geodetic
+ // The magnetic field at a given point, in nanoteslas in geodetic
// coordinates.
private float mX;
private float mY;
@@ -278,7 +278,7 @@
}
/**
- * @return Horizontal component of the field strength in nonoteslas.
+ * @return Horizontal component of the field strength in nanoteslas.
*/
public float getHorizontalStrength() {
return (float) Math.hypot(mX, mY);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f2e9078..8333b81 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -26,7 +26,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -3801,8 +3800,9 @@
private void unsupportedStartingFrom(int version) {
if (Process.myUid() == Process.SYSTEM_UID) {
- // The getApplicationInfo() call we make below is not supported in system context, and
- // we want to allow the system to use these APIs anyway.
+ // The getApplicationInfo() call we make below is not supported in system context. Let
+ // the call through here, and rely on the fact that ConnectivityService will refuse to
+ // allow the system to use these APIs anyway.
return;
}
@@ -3819,11 +3819,6 @@
// functions by accessing ConnectivityService directly. However, it should be clear that doing
// so is unsupported and may break in the future. http://b/22728205
private void checkLegacyRoutingApiAccess() {
- if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS")
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
unsupportedStartingFrom(VERSION_CODES.M);
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6bd2e76..8681893 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -30,6 +30,8 @@
import dalvik.system.VMRuntime;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -1083,7 +1085,67 @@
return true;
}
+ /** Build information for a particular device partition. */
+ public static class Partition {
+ /** The name identifying the system partition. */
+ public static final String PARTITION_NAME_SYSTEM = "system";
+
+ private String mName;
+ private String mFingerprint;
+ private long mTimeMs;
+
+ public Partition() {}
+
+ private Partition(String name, String fingerprint, long timeMs) {
+ mName = name;
+ mFingerprint = fingerprint;
+ mTimeMs = timeMs;
+ }
+
+ /** The name of this partition, e.g. "system", or "vendor" */
+ public String getName() {
+ return mName;
+ }
+
+ /** The build fingerprint of this partition, see {@link Build#FINGERPRINT}. */
+ public String getFingerprint() {
+ return mFingerprint;
+ }
+
+ /** The time (ms since epoch), at which this partition was built, see {@link Build#TIME}. */
+ public long getTimeMillis() {
+ return mTimeMs;
+ }
+ }
+
+ /**
+ * Get build information about partitions that have a separate fingerprint defined.
+ *
+ * The list includes partitions that are suitable candidates for over-the-air updates. This is
+ * not an exhaustive list of partitions on the device.
+ */
+ public static List<Partition> getPartitions() {
+ ArrayList<Partition> partitions = new ArrayList();
+
+ String[] names = new String[] {
+ "bootimage", "odm", "product", "product_services", Partition.PARTITION_NAME_SYSTEM,
+ "vendor"
+ };
+ for (String name : names) {
+ String fingerprint = SystemProperties.get("ro." + name + ".build.fingerprint");
+ if (TextUtils.isEmpty(fingerprint)) {
+ continue;
+ }
+ long time = getLong("ro." + name + ".build.date.utc") * 1000;
+ partitions.add(new Partition(name, fingerprint, time));
+ }
+
+ return partitions;
+ }
+
// The following properties only make sense for internal engineering builds.
+
+ /** The time at which the build was produced, given in milliseconds since the UNIX epoch. */
public static final long TIME = getLong("ro.build.date.utc") * 1000;
public static final String USER = getString("ro.build.user");
public static final String HOST = getString("ro.build.host");
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index f83acb6..54be639 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -45,6 +45,7 @@
private static final String TAG = "GraphicsEnvironment";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
private static final String ANGLE_PACKAGE_NAME = "com.android.angle";
+ private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE";
private ClassLoader mClassLoader;
private String mLayerPath;
@@ -123,7 +124,6 @@
}
}
}
-
}
// Include the app's lib directory in all cases
@@ -133,7 +133,7 @@
}
/**
- * Selectively enable ANGLE for applications
+ * Pass ANGLE details down to trigger enable logic
*/
private static void setupAngle(Context context) {
@@ -143,39 +143,67 @@
String packageName = context.getPackageName();
- // Only provide an ANGLE namespace if the package name matches setting
+ boolean devOptIn = false;
if ((angleEnabledApp != null && packageName != null)
&& (!angleEnabledApp.isEmpty() && !packageName.isEmpty())
&& angleEnabledApp.equals(packageName)) {
- if (DEBUG) Log.v(TAG, "ANGLE enabled for " + packageName);
+ if (DEBUG) Log.v(TAG, packageName + " opted in for ANGLE via Developer Setting");
- ApplicationInfo angleInfo;
-
- try {
- angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
- PackageManager.MATCH_SYSTEM_ONLY);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
- return;
- }
-
- String abi = chooseAbi(angleInfo);
-
- // Build a path that includes installed native libs and APK
- StringBuilder sb = new StringBuilder();
- sb.append(angleInfo.nativeLibraryDir)
- .append(File.pathSeparator)
- .append(angleInfo.sourceDir)
- .append("!/lib/")
- .append(abi);
- String paths = sb.toString();
-
- if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
-
- // Providing any path will trigger namespace creation
- setAnglePath(paths);
+ devOptIn = true;
}
+
+ ApplicationInfo appInfo;
+ try {
+ appInfo = context.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Failed to get info about current application: " + packageName);
+ return;
+ }
+
+ String appPref = "dontcare";
+ final BaseBundle metadata = appInfo.metaData;
+ if (metadata != null) {
+ final String glesMode = metadata.getString(GLES_MODE_METADATA_KEY);
+ if (glesMode != null) {
+ if (glesMode.equals("angle")) {
+ appPref = "angle";
+ if (DEBUG) Log.v(TAG, packageName + " opted for ANGLE via AndroidManifest");
+ } else if (glesMode.equals("native")) {
+ appPref = "native";
+ if (DEBUG) Log.v(TAG, packageName + " opted for NATIVE via AndroidManifest");
+ } else {
+ Log.w(TAG, "Unrecognized GLES_MODE (\"" + glesMode + "\") for " + packageName
+ + ". Supported values are \"angle\" or \"native\"");
+ }
+ }
+ }
+
+ ApplicationInfo angleInfo;
+ try {
+ angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME,
+ PackageManager.MATCH_SYSTEM_ONLY);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed");
+ return;
+ }
+
+ String abi = chooseAbi(angleInfo);
+
+ // Build a path that includes installed native libs and APK
+ StringBuilder sb = new StringBuilder();
+ sb.append(angleInfo.nativeLibraryDir)
+ .append(File.pathSeparator)
+ .append(angleInfo.sourceDir)
+ .append("!/lib/")
+ .append(abi);
+ String paths = sb.toString();
+
+ if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths);
+
+ // Further opt-in logic is handled in native, so pass relevant info down
+ setAngleInfo(paths, packageName, appPref, devOptIn);
}
/**
@@ -266,5 +294,6 @@
private static native void setLayerPaths(ClassLoader classLoader, String layerPaths);
private static native void setDebugLayers(String layers);
private static native void setDriverPath(String path);
- private static native void setAnglePath(String path);
+ private static native void setAngleInfo(String path, String appPackage, String appPref,
+ boolean devOptIn);
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 6fab3c4..0f64c45 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -483,6 +483,8 @@
* @param appDataDir null-ok the data directory of the app.
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param packagesForUid null-ok all the packages with the same uid as this process.
+ * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
@@ -501,10 +503,13 @@
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
+ @Nullable String[] packagesForUid,
+ @Nullable String[] visibleVols,
@Nullable String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, packageName,
+ packagesForUid, visibleVols, zygoteArgs);
}
/** @hide */
@@ -519,10 +524,13 @@
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
+ @Nullable String[] packagesForUid,
+ @Nullable String[] visibleVols,
@Nullable String[] zygoteArgs) {
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, packageName,
+ packagesForUid, visibleVols, zygoteArgs);
}
/**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 99181ac..7fd0a4b 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -215,6 +215,8 @@
* @param appDataDir null-ok the data directory of the app.
* @param invokeWith null-ok the command to invoke with.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param packagesForUid null-ok all the packages with the same uid as this process.
+ * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
* @return An object that describes the result of the attempt to start the process.
@@ -231,12 +233,14 @@
@Nullable String appDataDir,
@Nullable String invokeWith,
@Nullable String packageName,
+ @Nullable String[] packagesForUid,
+ @Nullable String[] visibleVols,
@Nullable String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
- packageName, zygoteArgs);
+ packageName, packagesForUid, visibleVols, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -355,6 +359,8 @@
* @param startChildZygote Start a sub-zygote. This creates a new zygote process
* that has its state cloned from this zygote process.
* @param packageName null-ok the name of the package this process belongs to.
+ * @param packagesForUid null-ok all the packages with the same uid as this process.
+ * @param visibleVols null-ok storage volumes that can be accessed by this process.
* @param extraArgs Additional arguments to supply to the zygote process.
* @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
@@ -372,6 +378,8 @@
@Nullable String invokeWith,
boolean startChildZygote,
@Nullable String packageName,
+ @Nullable String[] packagesForUid,
+ @Nullable String[] visibleVols,
@Nullable String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -439,6 +447,32 @@
argsForZygote.add("--package-name=" + packageName);
}
+ if (packagesForUid != null && packagesForUid.length > 0) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("--packages-for-uid=");
+
+ for (int i = 0; i < packagesForUid.length; ++i) {
+ if (i != 0) {
+ sb.append(',');
+ }
+ sb.append(packagesForUid[i]);
+ }
+ argsForZygote.add(sb.toString());
+ }
+
+ if (visibleVols != null && visibleVols.length > 0) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("--visible-vols=");
+
+ for (int i = 0; i < visibleVols.length; ++i) {
+ if (i != 0) {
+ sb.append(',');
+ }
+ sb.append(visibleVols[i]);
+ }
+ argsForZygote.add(sb.toString());
+ }
+
argsForZygote.add(processClass);
if (extraArgs != null) {
@@ -746,7 +780,8 @@
result = startViaZygote(processClass, niceName, uid, gid,
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
- true /* startChildZygote */, null /* packageName */, extraArgs);
+ true /* startChildZygote */, null /* packageName */,
+ null /* packagesForUid */, null /* visibleVolumes */, extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index d850e27..1f54ea5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -89,8 +89,13 @@
* @param appId The appId for the given package.
* @param sharedUserId The sharedUserId for given package if it specified
* {@code android:sharedUserId} in the manifest, otherwise {@code null}
- * @param userId
+ * @param userId The userId in which the storage needs to be mounted.
*/
public abstract void mountExternalStorageForApp(String packageName, int appId,
String sharedUserId, int userId);
+
+ /**
+ * @return Labels of storage volumes that are visible to the given userId.
+ */
+ public abstract String[] getVisibleVolumesForUser(int userId);
}
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index afd38369..e55afb6 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -157,7 +157,7 @@
public final DiskInfo disk;
public final String partGuid;
public int mountFlags = 0;
- public int mountUserId = -1;
+ public int mountUserId = UserHandle.USER_NULL;
@UnsupportedAppUsage
public int state = STATE_UNMOUNTED;
public String fsType;
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 82459b1..828fd73 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -1318,18 +1318,6 @@
}
public static final class Media implements AudioColumns {
-
- private static final String[] EXTERNAL_PATHS;
-
- static {
- String secondary_storage = System.getenv("SECONDARY_STORAGE");
- if (secondary_storage != null) {
- EXTERNAL_PATHS = secondary_storage.split(":");
- } else {
- EXTERNAL_PATHS = new String[0];
- }
- }
-
/**
* Get the content:// style URI for the audio media table on the
* given volume.
@@ -1343,14 +1331,9 @@
}
public static Uri getContentUriForPath(String path) {
- for (String ep : EXTERNAL_PATHS) {
- if (path.startsWith(ep)) {
- return EXTERNAL_CONTENT_URI;
- }
- }
-
- return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
- EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
+ return (path.startsWith(
+ Environment.getStorageDirectory().getAbsolutePath() + "/")
+ ? EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ad8626c..1d3cf19 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7267,12 +7267,12 @@
private static final Validator DOZE_DOUBLE_TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
/**
- * Whether the device should pulse on reach gesture.
+ * Gesture that wakes up the lock screen.
* @hide
*/
- public static final String DOZE_REACH_GESTURE = "doze_reach_gesture";
+ public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_lock_screen_gesture";
- private static final Validator DOZE_REACH_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+ private static final Validator DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
/**
* Gesture that wakes up the display, showing the ambient version of the status bar.
@@ -8193,7 +8193,7 @@
DOZE_ALWAYS_ON,
DOZE_PICK_UP_GESTURE,
DOZE_DOUBLE_TAP_GESTURE,
- DOZE_REACH_GESTURE,
+ DOZE_WAKE_LOCK_SCREEN_GESTURE,
DOZE_WAKE_SCREEN_GESTURE,
NFC_PAYMENT_DEFAULT_COMPONENT,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
@@ -8341,7 +8341,7 @@
VALIDATORS.put(DOZE_ALWAYS_ON, DOZE_ALWAYS_ON_VALIDATOR);
VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
- VALIDATORS.put(DOZE_REACH_GESTURE, DOZE_REACH_GESTURE_VALIDATOR);
+ VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR);
VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
@@ -9604,8 +9604,7 @@
* Use the old dnsmasq DHCP server for tethering instead of the framework implementation.
*
* Integer values are interpreted as boolean, and the absence of an explicit setting
- * is interpreted as |true|.
- * TODO: make the default |false|
+ * is interpreted as |false|.
* @hide
*/
public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER =
diff --git a/core/java/android/transition/ChangeImageTransform.java b/core/java/android/transition/ChangeImageTransform.java
index d7a9120..9fa9961 100644
--- a/core/java/android/transition/ChangeImageTransform.java
+++ b/core/java/android/transition/ChangeImageTransform.java
@@ -97,22 +97,13 @@
values.put(PROPNAME_BOUNDS, bounds);
Matrix matrix;
ImageView.ScaleType scaleType = imageView.getScaleType();
- if (scaleType == ImageView.ScaleType.FIT_XY) {
- matrix = imageView.getImageMatrix();
- if (!matrix.isIdentity()) {
- matrix = new Matrix(matrix);
- } else {
- int drawableWidth = drawable.getIntrinsicWidth();
- int drawableHeight = drawable.getIntrinsicHeight();
- if (drawableWidth > 0 && drawableHeight > 0) {
- float scaleX = ((float) bounds.width()) / drawableWidth;
- float scaleY = ((float) bounds.height()) / drawableHeight;
- matrix = new Matrix();
- matrix.setScale(scaleX, scaleY);
- } else {
- matrix = null;
- }
- }
+ int drawableWidth = drawable.getIntrinsicWidth();
+ int drawableHeight = drawable.getIntrinsicHeight();
+ if (scaleType == ImageView.ScaleType.FIT_XY && drawableWidth > 0 && drawableHeight > 0) {
+ float scaleX = ((float) bounds.width()) / drawableWidth;
+ float scaleY = ((float) bounds.height()) / drawableHeight;
+ matrix = new Matrix();
+ matrix.setScale(scaleX, scaleY);
} else {
matrix = new Matrix(imageView.getImageMatrix());
}
@@ -152,17 +143,13 @@
}
Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- if (startBounds == null || endBounds == null) {
+ Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+ Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
+ if (startBounds == null || endBounds == null || startMatrix == null || endMatrix == null) {
return null;
}
- Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
- Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
-
- boolean matricesEqual = (startMatrix == null && endMatrix == null) ||
- (startMatrix != null && startMatrix.equals(endMatrix));
-
- if (startBounds.equals(endBounds) && matricesEqual) {
+ if (startBounds.equals(endBounds) && startMatrix.equals(endMatrix)) {
return null;
}
@@ -172,15 +159,9 @@
int drawableHeight = drawable.getIntrinsicHeight();
ObjectAnimator animator;
- if (drawableWidth == 0 || drawableHeight == 0) {
+ if (drawableWidth <= 0 || drawableHeight <= 0) {
animator = createNullAnimator(imageView);
} else {
- if (startMatrix == null) {
- startMatrix = Matrix.IDENTITY_MATRIX;
- }
- if (endMatrix == null) {
- endMatrix = Matrix.IDENTITY_MATRIX;
- }
ANIMATED_TRANSFORM_PROPERTY.set(imageView, startMatrix);
animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
}
@@ -189,7 +170,7 @@
private ObjectAnimator createNullAnimator(ImageView imageView) {
return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
- NULL_MATRIX_EVALUATOR, null, null);
+ NULL_MATRIX_EVALUATOR, Matrix.IDENTITY_MATRIX, Matrix.IDENTITY_MATRIX);
}
private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index c4ef77a..4608e20 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -188,8 +188,12 @@
final ViewGroup sceneRoot = scene.getSceneRoot();
if (!sPendingTransitions.contains(sceneRoot)) {
+ Scene oldScene = Scene.getCurrentScene(sceneRoot);
if (transition == null) {
- exitPreviousScene(sceneRoot);
+ // Notify old scene that it is being exited
+ if (oldScene != null) {
+ oldScene.exit();
+ }
scene.enter();
} else {
@@ -198,7 +202,6 @@
Transition transitionClone = transition.clone();
transitionClone.setSceneRoot(sceneRoot);
- Scene oldScene = Scene.getCurrentScene(sceneRoot);
if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
transitionClone.setCanRemoveViews(true);
}
@@ -212,14 +215,6 @@
}
}
- private static void exitPreviousScene(final ViewGroup sceneRoot) {
- // Notify previous scene that it is being exited
- final Scene previousScene = Scene.getCurrentScene(sceneRoot);
- if (previousScene != null) {
- previousScene.exit();
- }
- }
-
@UnsupportedAppUsage
private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions =
@@ -349,7 +344,11 @@
transition.captureValues(sceneRoot, true);
}
- exitPreviousScene(sceneRoot);
+ // Notify previous scene that it is being exited
+ Scene previousScene = Scene.getCurrentScene(sceneRoot);
+ if (previousScene != null) {
+ previousScene.exit();
+ }
}
/**
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 5108a79..294a179 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -71,19 +71,19 @@
/**
* Maximum number of entries to have in array caches.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private static final int CACHE_SIZE = 10;
/**
* Special hash array value that indicates the container is immutable.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static final int[] EMPTY_IMMUTABLE_INTS = new int[0];
/**
* @hide Special immutable empty ArrayMap.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use your own singleton empty map.
public static final ArrayMap EMPTY = new ArrayMap<>(-1);
/**
@@ -92,21 +92,21 @@
* The first entry in the array is a pointer to the next array in the
* list; the second entry is a pointer to the int[] hash code array for it.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static Object[] mBaseCache;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static int mBaseCacheSize;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static Object[] mTwiceBaseCache;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
static int mTwiceBaseCacheSize;
final boolean mIdentityHashCode;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public key/value API.
int[] mHashes;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public key/value API.
Object[] mArray;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
int mSize;
MapCollections<K, V> mCollections;
@@ -122,7 +122,7 @@
}
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object).
int indexOf(Object key, int hash) {
final int N = mSize;
@@ -161,7 +161,7 @@
return ~end;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null)
int indexOfNull() {
final int N = mSize;
@@ -200,7 +200,7 @@
return ~end;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private void allocArrays(final int size) {
if (mHashes == EMPTY_IMMUTABLE_INTS) {
throw new UnsupportedOperationException("ArrayMap is immutable");
@@ -239,7 +239,7 @@
mArray = new Object[size<<1];
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
if (hashes.length == (BASE_SIZE*2)) {
synchronized (ArrayMap.class) {
@@ -393,8 +393,15 @@
: indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode());
}
- @UnsupportedAppUsage
- int indexOfValue(Object value) {
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified value, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(Object value) {
final int N = mSize*2;
final Object[] array = mArray;
if (value == null) {
@@ -551,7 +558,7 @@
* The array must already be large enough to contain the item.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use put(K, V).
public void append(K key, V value) {
int index = mSize;
final int hash = key == null ? 0
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 526a950..d74a0fe 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -71,15 +71,15 @@
static int sTwiceBaseCacheSize;
final boolean mIdentityHashCode;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public API.
int[] mHashes;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public API.
Object[] mArray;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
int mSize;
MapCollections<E, E> mCollections;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object).
private int indexOf(Object key, int hash) {
final int N = mSize;
@@ -118,7 +118,7 @@
return ~end;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null)
private int indexOfNull() {
final int N = mSize;
@@ -157,7 +157,7 @@
return ~end;
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private void allocArrays(final int size) {
if (size == (BASE_SIZE * 2)) {
synchronized (ArraySet.class) {
@@ -215,7 +215,7 @@
mArray = new Object[size];
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail.
private static void freeArrays(final int[] hashes, final Object[] array, final int size) {
if (hashes.length == (BASE_SIZE * 2)) {
synchronized (ArraySet.class) {
@@ -289,9 +289,10 @@
}
}
- /** {@hide} */
- @UnsupportedAppUsage
- public ArraySet(Collection<E> set) {
+ /**
+ * Create a new ArraySet with items from the given collection.
+ */
+ public ArraySet(Collection<? extends E> set) {
this();
if (set != null) {
addAll(set);
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index d5af922..af163ac 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -46,11 +46,11 @@
* @hide
*/
public class LongSparseLongArray implements Cloneable {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
private long[] mKeys;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
private long[] mValues;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public.
private int mSize;
/**
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index aa5ca35..89ea2d3 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -56,11 +56,11 @@
private static final Object DELETED = new Object();
private boolean mGarbage = false;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
private int[] mKeys;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, E)
private Object[] mValues;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
private int mSize;
/**
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index 9c6b969..d4c4095 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -185,7 +185,9 @@
return mValues[index];
}
- /** @hide */
+ /**
+ * Directly set the value at a particular index.
+ */
public void setValueAt(int index, boolean value) {
mValues[index] = value;
}
@@ -304,10 +306,10 @@
return buffer.toString();
}
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
private int[] mKeys;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, boolean)
private boolean[] mValues;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
private int mSize;
}
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index 1954753..9e6bad1 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -46,11 +46,11 @@
* order in the case of <code>valueAt(int)</code>.</p>
*/
public class SparseIntArray implements Cloneable {
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int)
private int[] mKeys;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, int)
private int[] mValues;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 28) // Use size()
private int mSize;
/**
@@ -191,7 +191,6 @@
/**
* Directly set the value at a particular index.
- * @hide
*/
public void setValueAt(int index, int value) {
mValues[index] = value;
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 1203541..1bbef8e 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -410,7 +410,7 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
@@ -423,7 +423,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateApkVerityRootHash(
+ return VerityBuilder.generateApkVerityRootHash(
apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
}
}
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 939522d..1471870 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -534,7 +534,7 @@
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
- return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+ return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
@@ -547,7 +547,7 @@
if (vSigner.verityRootHash == null) {
return null;
}
- return ApkVerityBuilder.generateApkVerityRootHash(
+ return VerityBuilder.generateApkVerityRootHash(
apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo);
}
}
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 081033a..87af5364 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -332,7 +332,7 @@
try {
byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest,
apk.length(), signatureInfo);
- ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerityTree(apk,
+ VerityBuilder.VerityResult verity = VerityBuilder.generateApkVerityTree(apk,
signatureInfo, new ByteBufferFactory() {
@Override
public ByteBuffer create(int capacity) {
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
similarity index 93%
rename from core/java/android/util/apk/ApkVerityBuilder.java
rename to core/java/android/util/apk/VerityBuilder.java
index edd09f8..443bbd8 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -29,19 +29,18 @@
import java.util.ArrayList;
/**
- * ApkVerityBuilder builds the APK verity tree and the verity header. The generated tree format can
- * be stored on disk for apk-verity setup and used by kernel. Note that since the current
- * implementation is different from the upstream, we call this implementation apk-verity instead of
- * fs-verity.
+ * VerityBuilder builds the verity Merkle tree and other metadata. The generated tree format can
+ * be stored on disk for fs-verity setup and used by kernel. The builder support standard
+ * fs-verity, and Android specific apk-verity that requires additional kernel patches.
*
- * <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to
- * the existing APK format, it has to skip APK Signing Block and also has some special treatment for
- * the "Central Directory offset" field of ZIP End of Central Directory.
+ * <p>Unlike a regular Merkle tree of fs-verity, the apk-verity tree does not cover the file content
+ * fully, and has to skip APK Signing Block with some special treatment for the "Central Directory
+ * offset" field of ZIP End of Central Directory.
*
* @hide
*/
-public abstract class ApkVerityBuilder {
- private ApkVerityBuilder() {}
+public abstract class VerityBuilder {
+ private VerityBuilder() {}
private static final int CHUNK_SIZE_BYTES = 4096; // Typical Linux block size
private static final int DIGEST_SIZE_BYTES = 32; // SHA-256 size
@@ -52,7 +51,7 @@
private static final byte[] DEFAULT_SALT = new byte[8];
/** Result generated by the builder. */
- public static class ApkVerityResult {
+ public static class VerityResult {
/** Raw fs-verity metadata and Merkle tree ready to be deployed on disk. */
public final ByteBuffer verityData;
@@ -62,7 +61,7 @@
/** Root hash of the Merkle tree. */
public final byte[] rootHash;
- private ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
+ private VerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
this.verityData = verityData;
this.merkleTreeSize = merkleTreeSize;
this.rootHash = rootHash;
@@ -74,14 +73,14 @@
* ByteBuffer} created by the {@link ByteBufferFactory}. The output is suitable to be used as
* the on-disk format for fs-verity to use.
*
- * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+ * @return VerityResult containing a buffer with the generated Merkle tree stored at the
* front, the tree size, and the calculated root hash.
*/
@NonNull
- public static ApkVerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
+ public static VerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
@NonNull ByteBufferFactory bufferFactory)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
- return generateVerityTree(apk, bufferFactory, null /* signatureInfo */,
+ return generateVerityTreeInternal(apk, bufferFactory, null /* signatureInfo */,
false /* skipSigningBlock */);
}
@@ -91,18 +90,19 @@
* Block specificed in {@code signatureInfo}. The output is suitable to be used as the on-disk
* format for fs-verity to use (with elide and patch extensions).
*
- * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+ * @return VerityResult containing a buffer with the generated Merkle tree stored at the
* front, the tree size, and the calculated root hash.
*/
@NonNull
- public static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
+ public static VerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
@Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
- return generateVerityTree(apk, bufferFactory, signatureInfo, true /* skipSigningBlock */);
+ return generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
+ true /* skipSigningBlock */);
}
@NonNull
- private static ApkVerityResult generateVerityTree(@NonNull RandomAccessFile apk,
+ private static VerityResult generateVerityTreeInternal(@NonNull RandomAccessFile apk,
@NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo,
boolean skipSigningBlock)
throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
@@ -124,7 +124,7 @@
byte[] salt = skipSigningBlock ? DEFAULT_SALT : null;
byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset,
tree, skipSigningBlock);
- return new ApkVerityResult(output, merkleTreeSize, apkRootHash);
+ return new VerityResult(output, merkleTreeSize, apkRootHash);
}
static void generateApkVerityFooter(@NonNull RandomAccessFile apk,
@@ -173,7 +173,7 @@
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
- ApkVerityResult result = generateVerityTree(apk, bufferFactory, signatureInfo,
+ VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo,
true /* skipSigningBlock */);
ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
result.verityData.limit());
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 78e6dd8..b2944d6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -15636,7 +15636,7 @@
/**
* Sets the visual z position of this view, in pixels. This is equivalent to setting the
* {@link #setTranslationZ(float) translationZ} property to be the difference between
- * the x value passed in and the current {@link #getElevation() elevation} property.
+ * the z value passed in and the current {@link #getElevation() elevation} property.
*
* @param z The visual z position of this view, in pixels.
*/
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 0fef9a5..12cc54d 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -1338,7 +1338,9 @@
return;
}
if (matrix == null) {
- mDrawable.setBounds(0, 0, getWidth(), getHeight());
+ final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
+ final int vheight = getHeight() - mPaddingTop - mPaddingBottom;
+ mDrawable.setBounds(0, 0, vwidth, vheight);
} else {
mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
if (mDrawMatrix == null) {
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index cb282b6..0080ace 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -66,13 +66,13 @@
return !TextUtils.isEmpty(doubleTapSensorType());
}
- public boolean reachGestureEnabled(int user) {
- return boolSettingDefaultOn(Settings.Secure.DOZE_REACH_GESTURE, user)
- && reachGestureAvailable();
+ public boolean wakeLockScreenGestureEnabled(int user) {
+ return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user)
+ && wakeLockScreenGestureAvailable();
}
- public boolean reachGestureAvailable() {
- return !TextUtils.isEmpty(reachSensorType());
+ public boolean wakeLockScreenGestureAvailable() {
+ return !TextUtils.isEmpty(wakeLockScreenSensorType());
}
public boolean wakeScreenGestureEnabled(int user) {
@@ -92,8 +92,8 @@
return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
}
- public String reachSensorType() {
- return mContext.getResources().getString(R.string.config_dozeReachSensorType);
+ public String wakeLockScreenSensorType() {
+ return mContext.getResources().getString(R.string.config_dozeWakeLockScreenSensorType);
}
public String wakeScreenSensorType() {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 927322e..98b7b5d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -133,15 +133,16 @@
* if this is the parent, or -1 on error.
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
- int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- String packageName) {
+ int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+ String packageName, String[] packagesForUid, String[] visibleVolIds) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName);
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
+ packagesForUid, visibleVolIds);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -154,9 +155,9 @@
}
native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
- int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- String packageName);
+ int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+ String packageName, String[] packagesForUid, String[] visibleVolIds);
/**
* Called to do any initialization before starting an application.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 06c41d8..4a94ec4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -241,7 +241,8 @@
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
- parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName);
+ parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName,
+ parsedArgs.packagesForUid, parsedArgs.visibleVolIds);
try {
if (pid == 0) {
@@ -432,6 +433,12 @@
/** from --package-name */
String packageName;
+ /** from --packages-for-uid */
+ String[] packagesForUid;
+
+ /** from --visible-vols */
+ String[] visibleVolIds;
+
/**
* Any args after and including the first non-option arg
* (or after a '--')
@@ -687,6 +694,10 @@
throw new IllegalArgumentException("Duplicate arg specified");
}
packageName = arg.substring(arg.indexOf('=') + 1);
+ } else if (arg.startsWith("--packages-for-uid=")) {
+ packagesForUid = arg.substring(arg.indexOf('=') + 1).split(",");
+ } else if (arg.startsWith("--visible-vols=")) {
+ visibleVolIds = arg.substring(arg.indexOf('=') + 1).split(",");
} else {
break;
}
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index 6df23f7..a1f2377 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -58,6 +58,11 @@
int ret;
int fd;
+ if (name == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return;
+ }
+
fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index e3bec3c..b70485d 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -28,9 +28,12 @@
android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str());
}
-void setAnglePath(JNIEnv* env, jobject clazz, jstring path) {
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn) {
ScopedUtfChars pathChars(env, path);
- android::GraphicsEnv::getInstance().setAnglePath(pathChars.c_str());
+ ScopedUtfChars appNameChars(env, appName);
+ ScopedUtfChars appPrefChars(env, appPref);
+ android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(),
+ appPrefChars.c_str(), devOptIn);
}
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -49,7 +52,7 @@
const JNINativeMethod g_methods[] = {
{ "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) },
- { "setAnglePath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setAnglePath) },
+ { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", reinterpret_cast<void*>(setAngleInfo_native) },
{ "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) },
{ "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) },
};
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index c15b7ee..109e65c 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -285,10 +285,6 @@
static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstring path,
jint limitUid, jobjectArray limitIfacesObj, jint limitTag,
jboolean useBpfStats) {
- ScopedUtfChars path8(env, path);
- if (path8.c_str() == NULL) {
- return -1;
- }
std::vector<std::string> limitIfaces;
if (limitIfacesObj != NULL && env->GetArrayLength(limitIfacesObj) > 0) {
@@ -308,6 +304,11 @@
if (parseBpfNetworkStatsDetail(&lines, limitIfaces, limitTag, limitUid) < 0)
return -1;
} else {
+ ScopedUtfChars path8(env, path);
+ if (path8.c_str() == NULL) {
+ ALOGE("the qtaguid legacy path is invalid: %s", path8.c_str());
+ return -1;
+ }
if (legacyReadNetworkStatsDetail(&lines, limitIfaces, limitTag,
limitUid, path8.c_str()) < 0)
return -1;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 364393e..1f95862 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -382,11 +382,10 @@
return 0;
}
-static bool createPkgSandbox(uid_t uid, const char* package_name, std::string& pkg_sandbox_dir,
- std::string* error_msg) {
+static bool createPkgSandbox(uid_t uid, const std::string& package_name, std::string* error_msg) {
// Create /mnt/user/0/package/<package-name>
userid_t user_id = multiuser_get_user_id(uid);
- StringAppendF(&pkg_sandbox_dir, "/%d", user_id);
+ std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0751, AID_ROOT, AID_ROOT) != 0) {
*error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
return false;
@@ -396,7 +395,7 @@
*error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
return false;
}
- StringAppendF(&pkg_sandbox_dir, "/%s", package_name);
+ StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0755, uid, uid) != 0) {
*error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
return false;
@@ -404,10 +403,51 @@
return true;
}
+static bool mountPkgSpecificDir(const std::string& mntSourceRoot,
+ const std::string& mntTargetRoot, const std::string& packageName,
+ const char* dirName, std::string* error_msg) {
+ std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
+ mntSourceRoot.c_str(), dirName, packageName.c_str());
+ std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
+ mntTargetRoot.c_str(), dirName, packageName.c_str());
+ if (TEMP_FAILURE_RETRY(mount(mntSourceDir.c_str(), mntTargetDir.c_str(),
+ nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
+ mntSourceDir.c_str(), mntTargetDir.c_str(), strerror(errno));
+ return false;
+ }
+ if (TEMP_FAILURE_RETRY(mount(nullptr, mntTargetDir.c_str(),
+ nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", mntTargetDir.c_str());
+ return false;
+ }
+ return true;
+}
+
+static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
+ const std::vector<std::string>& volumeLabels, userid_t userId, std::string* error_msg) {
+ for (auto& label : volumeLabels) {
+ std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
+ std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
+ if (label == "emulated") {
+ StringAppendF(&mntSource, "/%d", userId);
+ StringAppendF(&mntTarget, "/%d", userId);
+ }
+ for (auto& package : packageNames) {
+ mountPkgSpecificDir(mntSource, mntTarget, package, "data", error_msg);
+ mountPkgSpecificDir(mntSource, mntTarget, package, "media", error_msg);
+ mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+ }
+ }
+ return true;
+}
+
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
- bool force_mount_namespace, std::string* error_msg, const char* package_name) {
+ bool force_mount_namespace, std::string* error_msg, const std::string& package_name,
+ const std::vector<std::string>& packages_for_uid,
+ const std::vector<std::string>& visible_vol_ids) {
// See storage config details at http://source.android.com/tech/storage/
String8 storageSource;
@@ -459,12 +499,25 @@
return false;
}
} else {
- if (package_name == nullptr) {
+ if (package_name.empty()) {
return true;
}
- std::string pkgSandboxDir("/mnt/user");
- if (!createPkgSandbox(uid, package_name, pkgSandboxDir, error_msg)) {
- return false;
+ userid_t user_id = multiuser_get_user_id(uid);
+ std::string pkgSandboxDir = StringPrintf("/mnt/user/%d/package/%s",
+ user_id, package_name.c_str());
+ struct stat sb;
+ bool sandboxAlreadyCreated = true;
+ if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) {
+ if (errno == ENOENT) {
+ ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
+ sandboxAlreadyCreated = false;
+ if (!createPkgSandbox(uid, package_name, error_msg)) {
+ return false;
+ }
+ } else {
+ ALOGE("Failed to lstat %s", pkgSandboxDir.c_str());
+ return false;
+ }
}
if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
@@ -472,6 +525,15 @@
pkgSandboxDir.c_str(), strerror(errno));
return false;
}
+ // If the sandbox was already created by vold, only then set up the bind mounts for
+ // pkg specific directories. Otherwise, leave as is and bind mounts will be taken
+ // care of by vold later.
+ if (sandboxAlreadyCreated) {
+ if (!preparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
+ user_id, error_msg)) {
+ return false;
+ }
+ }
}
} else {
if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
@@ -611,7 +673,8 @@
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external, jstring java_se_info, jstring java_se_name,
bool is_system_server, bool is_child_zygote, jstring instructionSet,
- jstring dataDir, jstring packageName) {
+ jstring dataDir, jstring packageName, jobjectArray packagesForUid,
+ jobjectArray visibleVolIds) {
std::string error_msg;
auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
@@ -661,17 +724,33 @@
ALOGW("Native bridge will not be used because dataDir == NULL.");
}
- ScopedUtfChars* package_name = nullptr;
- const char* package_name_c_str = nullptr;
+ std::string package_name_str("");
if (packageName != nullptr) {
- package_name = new ScopedUtfChars(env, packageName);
- package_name_c_str = package_name->c_str();
+ ScopedUtfChars package(env, packageName);
+ package_name_str = package.c_str();
} else if (is_system_server) {
- package_name_c_str = "android";
+ package_name_str = "android";
+ }
+ std::vector<std::string> packages_for_uid;
+ if (packagesForUid != nullptr) {
+ jsize count = env->GetArrayLength(packagesForUid);
+ for (jsize i = 0; i < count; ++i) {
+ jstring package_for_uid = (jstring) env->GetObjectArrayElement(packagesForUid, i);
+ ScopedUtfChars package(env, package_for_uid);
+ packages_for_uid.push_back(package.c_str());
+ }
+ }
+ std::vector<std::string> visible_vol_ids;
+ if (visibleVolIds != nullptr) {
+ jsize count = env->GetArrayLength(visibleVolIds);
+ for (jsize i = 0; i < count; ++i) {
+ jstring visible_vol_id = (jstring) env->GetObjectArrayElement(visibleVolIds, i);
+ ScopedUtfChars vol(env, visible_vol_id);
+ visible_vol_ids.push_back(vol.c_str());
+ }
}
bool success = MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
- package_name_c_str);
- delete package_name;
+ package_name_str, packages_for_uid, visible_vol_ids);
if (!success) {
ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
if (errno == ENOTCONN || errno == EROFS) {
@@ -936,7 +1015,8 @@
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
- jstring instructionSet, jstring appDataDir, jstring packageName) {
+ jstring instructionSet, jstring appDataDir, jstring packageName,
+ jobjectArray packagesForUid, jobjectArray visibleVolIds) {
jlong capabilities = 0;
// Grant CAP_WAKE_ALARM to the Bluetooth process.
@@ -989,7 +1069,8 @@
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, se_name, false,
- is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName);
+ is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName,
+ packagesForUid, visibleVolIds);
}
return pid;
}
@@ -1003,7 +1084,7 @@
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
- false, NULL, NULL, nullptr);
+ false, NULL, NULL, nullptr, nullptr, nullptr);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -1084,7 +1165,7 @@
{ "nativeSecurityInit", "()V",
(void *) com_android_internal_os_Zygote_nativeSecurityInit },
{ "nativeForkAndSpecialize",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index f9f725a..14ed9e6 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -397,6 +397,9 @@
// Ordered GPU debug layer list
// i.e. <layer1>:<layer2>:...:<layerN>
optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ // App will load ANGLE instead of native GLES drivers.
+ optional SettingProto angle_enabled_app = 3;
}
optional Gpu gpu = 59;
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
new file mode 100644
index 0000000..941c81f
--- /dev/null
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.server.usage;
+import "frameworks/base/core/proto/android/content/configuration.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message IntervalStatsProto {
+ message StringPool {
+ optional int32 size = 1;
+ repeated string strings = 2;
+ }
+
+ message CountAndTime {
+ optional int32 count = 1;
+ optional int64 time_ms = 2;
+ }
+
+ // Stores the relevant information from a UsageStats
+ message UsageStats {
+ message ChooserAction {
+ message CategoryCount {
+ optional string name = 1;
+ optional int32 count = 3;
+ }
+ optional string name = 1;
+ repeated CategoryCount counts = 3;
+ }
+ optional string package = 1;
+ // package_index contains the index + 1 of the package name in the string pool
+ optional int32 package_index = 2;
+ optional int64 last_time_active_ms = 3;
+ optional int64 total_time_active_ms = 4;
+ optional int32 last_event = 5;
+ optional int32 app_launch_count = 6;
+ repeated ChooserAction chooser_actions = 7;
+ }
+
+ // Stores the relevant information an IntervalStats will have about a Configuration
+ message Configuration {
+ optional .android.content.ConfigurationProto config = 1;
+ optional int64 last_time_active_ms = 2;
+ optional int64 total_time_active_ms = 3;
+ optional int32 count = 4;
+ optional bool active = 5;
+ }
+
+ // Stores the relevant information from a UsageEvents.Event
+ message Event {
+ optional string package = 1;
+ // package_index contains the index + 1 of the package name in the string pool
+ optional int32 package_index = 2;
+ optional string class = 3;
+ // class_index contains the index + 1 of the class name in the string pool
+ optional int32 class_index = 4;
+ optional int64 time_ms = 5;
+ optional int32 flags = 6;
+ optional int32 type = 7;
+ optional .android.content.ConfigurationProto config = 8;
+ optional string shortcut_id = 9;
+ optional int32 standby_bucket = 11;
+ optional string notification_channel = 12;
+ // notification_channel_index contains the index + 1 of the channel name in the string pool
+ optional int32 notification_channel_index = 13;
+ }
+
+ // The following fields contain supplemental data used to build IntervalStats, such as a string
+ // pool.
+ optional int64 end_time_ms = 1;
+ // stringpool contains all the package and class names used by UsageStats and Event
+ // They will hold a number that is equal to the index + 1 of their string in the pool
+ optional StringPool stringpool = 2;
+
+ // The following fields contain aggregated usage stats data
+ optional CountAndTime interactive = 10;
+ optional CountAndTime non_interactive = 11;
+ optional CountAndTime keyguard_shown = 12;
+ optional CountAndTime keyguard_hidden = 13;
+
+ // The following fields contain listed usage stats data
+ repeated UsageStats packages = 20;
+ repeated Configuration configurations = 21;
+ repeated Event event_log = 22;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 85a52d5..2a0e482 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -941,7 +941,6 @@
Requesting this by itself is not sufficient to give you
location access.
<p>Protection level: dangerous
- @hide
-->
<permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
android:permissionGroup="android.permission-group.LOCATION"
@@ -1186,9 +1185,9 @@
android:priority="700" />
<!-- Required to be able to access the camera device.
- <p>This will automatically enforce the <a
- href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
- <uses-feature>}</a> manifest element for <em>all</em> camera features.
+ <p>This will automatically enforce the
+ <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+ uses-feature</a> manifest element for <em>all</em> camera features.
If you do not require all camera features or can properly operate if a camera
is not available, then you must modify your manifest as appropriate in order to
install on devices that don't support all camera features.</p>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6ae2541..bebd489 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -531,7 +531,7 @@
<skip />
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"चेहरे की पहचान की गई"</string>
- <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरा की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
+ <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहित नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 41616df..912db20 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -529,7 +529,7 @@
<string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmadı"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Parmak izi kimlik doğrulaması yapıldı"</string>
<string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Yüz kimliği doğrulandı"</string>
- <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Yüz kimliği doğrulandı, lütfen doğrula\'ya basın"</string>
+ <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Parmak izi donanımı kullanılamıyor."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Parmak izi depolanamıyor. Lütfen mevcut parmak izlerinden birini kaldırın."</string>
<string name="fingerprint_error_timeout" msgid="3927186043737732875">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 65b8807..e0db946 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1425,7 +1425,7 @@
at {@link android.view.inputmethod.InputConnection#performEditorAction(int)
InputConnection.performEditorAction(int)}.
<p>Corresponds to
- {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_FULLSCREEN}. -->
+ {@link android.view.inputmethod.EditorInfo#IME_FLAG_NAVIGATE_PREVIOUS}. -->
<flag name="flagNavigatePrevious" value="0x4000000" />
<!-- Used to specify that there is something
interesting that a forward navigation can focus on. This is like using
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9aebf6c..0daf5a7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2113,8 +2113,8 @@
<!-- Type of the long press sensor. Empty if long press is not supported. -->
<string name="config_dozeLongPressSensorType" translatable="false"></string>
- <!-- Type of the reach sensor. Empty if reach is not supported. -->
- <string name="config_dozeReachSensorType" translatable="false"></string>
+ <!-- Type of sensor that wakes up the lock screen. Empty if not supported. -->
+ <string name="config_dozeWakeLockScreenSensorType" translatable="false"></string>
<!-- Type of the wake up sensor. Empty if not supported. -->
<string name="config_dozeWakeScreenSensorType" translatable="false"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9f2256a..72ae0d6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3279,7 +3279,7 @@
<java-symbol type="array" name="config_hideWhenDisabled_packageNames" />
<java-symbol type="string" name="config_dozeLongPressSensorType" />
- <java-symbol type="string" name="config_dozeReachSensorType" />
+ <java-symbol type="string" name="config_dozeWakeLockScreenSensorType" />
<java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
<java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java
index 1349844..1286b13 100644
--- a/core/tests/coretests/src/android/net/LocalSocketTest.java
+++ b/core/tests/coretests/src/android/net/LocalSocketTest.java
@@ -22,6 +22,7 @@
import android.net.LocalSocketAddress;
import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest;
+
import junit.framework.TestCase;
import java.io.FileDescriptor;
@@ -39,6 +40,20 @@
ls = new LocalSocket();
+ try {
+ ls.connect(new LocalSocketAddress(null));
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // pass
+ }
+
+ try {
+ ls.bind(new LocalSocketAddress(null));
+ fail("Expected NullPointerException");
+ } catch (NullPointerException e) {
+ // pass
+ }
+
ls.connect(new LocalSocketAddress("android.net.LocalSocketTest"));
ls1 = ss.accept();
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 4fec33f..c4dc0ad 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -23,8 +23,11 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
+import android.util.proto.WireTypeMismatchException;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -232,6 +235,40 @@
}
/**
+ * Read from a protocol buffer input stream.
+ * Protocol buffer message definition at {@link android.graphics.RectProto}
+ *
+ * @param proto Stream to read the Rect object from.
+ * @param fieldId Field Id of the Rect as defined in the parent message
+ * @hide
+ */
+ public void readFromProto(@NonNull ProtoInputStream proto, long fieldId) throws IOException,
+ WireTypeMismatchException {
+ final long token = proto.start(fieldId);
+ try {
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) RectProto.LEFT:
+ left = proto.readInt(RectProto.LEFT);
+ break;
+ case (int) RectProto.TOP:
+ top = proto.readInt(RectProto.TOP);
+ break;
+ case (int) RectProto.RIGHT:
+ right = proto.readInt(RectProto.RIGHT);
+ break;
+ case (int) RectProto.BOTTOM:
+ bottom = proto.readInt(RectProto.BOTTOM);
+ break;
+ }
+ }
+ } finally {
+ // Let caller handle any exceptions
+ proto.end(token);
+ }
+ }
+
+ /**
* Returns true if the rectangle is empty (left >= right or top >= bottom)
*/
public final boolean isEmpty() {
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index ed4da22..340f279 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -336,7 +336,7 @@
*
* <table border="0" cellspacing="0" cellpadding="0">
* <tr><td>Method Name </p></td>
- * <td>Valid Sates </p></td>
+ * <td>Valid States </p></td>
* <td>Invalid States </p></td>
* <td>Comments </p></td></tr>
* <tr><td>attachAuxEffect </p></td>
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 84d246f..dfcbabe 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -36,8 +36,6 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
@@ -60,7 +58,6 @@
import dalvik.system.CloseGuard;
import libcore.io.IoBridge;
-import libcore.io.Streams;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -335,19 +332,14 @@
final String msg = "Cannot set AudioAttributes to null";
throw new IllegalArgumentException(msg);
}
- Parcel pattributes = Parcel.obtain();
- attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
- setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
- pattributes.recycle();
+ setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, attributes);
}
});
}
@Override
public @NonNull AudioAttributes getAudioAttributes() {
- Parcel pattributes = getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
- AudioAttributes attributes = AudioAttributes.CREATOR.createFromParcel(pattributes);
- pattributes.recycle();
+ AudioAttributes attributes = (AudioAttributes) getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
return attributes;
}
@@ -1588,9 +1580,9 @@
* @param value value of the parameter to be set.
* @return true if the parameter is set successfully, false otherwise
*/
- private native boolean setParameter(int key, Parcel value);
+ private native boolean setParameter(int key, Object value);
- private native Parcel getParameter(int key);
+ private native Object getParameter(int key);
/**
@@ -3689,7 +3681,7 @@
supportedSchemes[i]);
}
- Log.v(TAG, "DrmInfoImpl() Parcel psshsize: " + pssh.length +
+ Log.v(TAG, "DrmInfoImpl() psshsize: " + pssh.length +
" supportedDRMsCount: " + supportedDRMsCount);
}
@@ -3954,7 +3946,7 @@
connection.setReadTimeout(TIMEOUT_MS);
connection.connect();
- response = Streams.readFully(connection.getInputStream());
+ response = readInputStreamFully(connection.getInputStream());
Log.v(TAG, "HandleProvisioninig: Thread run: response " +
response.length + " " + response);
@@ -4034,6 +4026,29 @@
finished = true;
} // run()
+ /**
+ * Returns a byte[] containing the remainder of 'in', closing it when done.
+ */
+ private byte[] readInputStreamFully(InputStream in) throws IOException {
+ try {
+ return readInputStreamFullyNoClose(in);
+ } finally {
+ in.close();
+ }
+ }
+
+ /**
+ * Returns a byte[] containing the remainder of 'in'.
+ */
+ private byte[] readInputStreamFullyNoClose(InputStream in) throws IOException {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
+ }
+ return bytes.toByteArray();
+ }
} // ProvisioningThread
private int HandleProvisioninig(UUID uuid) {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 0ff2d8f..c537945 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -160,8 +160,9 @@
public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
- private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio";
- private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio";
+ private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio";
+ private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio";
+ private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio";
private static String sLastInternalScanFingerprint;
private static final String[] ID3_GENRES = {
@@ -1193,6 +1194,9 @@
if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
|| path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
|| path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
+ || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR)
+ || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR)
+ || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
|| path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
|| path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
|| path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 1a844cc..693a3d0 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -1490,8 +1490,8 @@
{"_release", "()V", (void *)android_media_MediaPlayer2_release},
{"_reset", "()V", (void *)android_media_MediaPlayer2_reset},
{"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer2_getAudioStreamType},
- {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer2_setParameter},
- {"getParameter", "(I)Landroid/os/Parcel;", (void *)android_media_MediaPlayer2_getParameter},
+ {"setParameter", "(ILjava/lang/Object;)Z", (void *)android_media_MediaPlayer2_setParameter},
+ {"getParameter", "(I)Ljava/lang/Object;", (void *)android_media_MediaPlayer2_getParameter},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping},
{"_setVolume", "(FF)V", (void *)android_media_MediaPlayer2_setVolume},
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index ee4c954..89438e5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -15,6 +15,7 @@
"SettingsLibHelpUtils",
"SettingsLibRestrictedLockUtils",
"SettingsLibAppPreference",
+ "SettingsLibSearchWidget",
],
// ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
index a034d29..2f576e6 100644
--- a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
+++ b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="help_feedback_label" msgid="4550436169116444686">"Help en feedback"</string>
+ <string name="help_feedback_label" msgid="4550436169116444686">"Hulp en feedback"</string>
</resources>
diff --git a/packages/SettingsLib/SearchWidget/Android.bp b/packages/SettingsLib/SearchWidget/Android.bp
new file mode 100644
index 0000000..7541ca45
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/Android.bp
@@ -0,0 +1,8 @@
+android_library {
+ name: "SettingsLibSearchWidget",
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+ sdk_version: "system_current",
+ min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SearchWidget/AndroidManifest.xml b/packages/SettingsLib/SearchWidget/AndroidManifest.xml
new file mode 100644
index 0000000..b86544e
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.search">
+
+ <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
new file mode 100644
index 0000000..7e65848
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
+</vector>
diff --git a/packages/SettingsLib/SearchWidget/res/values/strings.xml b/packages/SettingsLib/SearchWidget/res/values/strings.xml
new file mode 100644
index 0000000..0b12810
--- /dev/null
+++ b/packages/SettingsLib/SearchWidget/res/values/strings.xml
@@ -0,0 +1,20 @@
+<!--
+ Copyright (C) 2018 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">
+ <!-- Text used as a search hint into the search box [CHAR_LIMIT=60]-->
+ <string name="search_menu">Search settings</string>
+</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 332ced6..508adbd 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1116,4 +1116,6 @@
<!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
<string name="time_unit_just_now">Just now</string>
- </resources>
+ <!-- The notice header of Third-party licenses. not translatable -->
+ <string name="notice_header" translatable="false"></string>
+</resources>
diff --git a/packages/SettingsLib/search/Android.mk b/packages/SettingsLib/search/Android.mk
index cb19891..14f9626 100644
--- a/packages/SettingsLib/search/Android.mk
+++ b/packages/SettingsLib/search/Android.mk
@@ -5,6 +5,9 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := \
+ $(LOCAL_PATH)/main/res
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 0c29f43..9653972 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -30,6 +30,7 @@
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothPbap;
import android.bluetooth.BluetoothPbapClient;
+import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
@@ -98,6 +99,7 @@
private PbapClientProfile mPbapClientProfile;
private PbapServerProfile mPbapProfile;
private HearingAidProfile mHearingAidProfile;
+ private SapProfile mSapProfile;
/**
* Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -210,6 +212,13 @@
addProfile(mPbapClientProfile, PbapClientProfile.NAME,
BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
}
+ if (mSapProfile == null && supportedList.contains(BluetoothProfile.SAP)) {
+ if (DEBUG) {
+ Log.d(TAG, "Adding local SAP profile");
+ }
+ mSapProfile = new SapProfile(mContext, mDeviceManager, this);
+ addProfile(mSapProfile, SapProfile.NAME, BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
+ }
mEventManager.registerProfileIntentReceiver();
}
@@ -550,6 +559,11 @@
removedProfiles.remove(mHearingAidProfile);
}
+ if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) {
+ profiles.add(mSapProfile);
+ removedProfiles.remove(mSapProfile);
+ }
+
if (DEBUG) {
Log.d(TAG,"New Profiles" + profiles.toString());
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 9a6f104..b4acc48 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -36,12 +36,10 @@
*/
final class SapProfile implements LocalBluetoothProfile {
private static final String TAG = "SapProfile";
- private static boolean V = true;
private BluetoothSap mService;
private boolean mIsProfileReady;
- private final LocalBluetoothAdapter mLocalAdapter;
private final CachedBluetoothDeviceManager mDeviceManager;
private final LocalBluetoothProfileManager mProfileManager;
@@ -59,7 +57,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (V) Log.d(TAG,"Bluetooth service connected");
+ Log.d(TAG, "Bluetooth service connected, profile:" + profile);
mService = (BluetoothSap) proxy;
// We just bound to the service, so refresh the UI for any connected SAP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,7 +79,7 @@
}
public void onServiceDisconnected(int profile) {
- if (V) Log.d(TAG,"Bluetooth service disconnected");
+ Log.d(TAG, "Bluetooth service disconnected, profile:" + profile);
mProfileManager.callServiceDisconnectedListeners();
mIsProfileReady=false;
}
@@ -96,13 +94,11 @@
return BluetoothProfile.SAP;
}
- SapProfile(Context context, LocalBluetoothAdapter adapter,
- CachedBluetoothDeviceManager deviceManager,
+ SapProfile(Context context, CachedBluetoothDeviceManager deviceManager,
LocalBluetoothProfileManager profileManager) {
- mLocalAdapter = adapter;
mDeviceManager = deviceManager;
mProfileManager = profileManager;
- mLocalAdapter.getProfileProxy(context, new SapServiceListener(),
+ BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new SapServiceListener(),
BluetoothProfile.SAP);
}
@@ -115,50 +111,47 @@
}
public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> sinks = mService.getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- mService.disconnect(sink);
- }
+ if (mService == null) {
+ return false;
}
return mService.connect(device);
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
- if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
- }
- return mService.disconnect(device);
- } else {
+ if (mService == null) {
return false;
}
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
}
public int getConnectionStatus(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
-
- return !deviceList.isEmpty() && deviceList.get(0).equals(device)
- ? mService.getConnectionState(device)
- : BluetoothProfile.STATE_DISCONNECTED;
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
}
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
+ if (mService == null) {
+ return false;
+ }
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
}
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return BluetoothProfile.PRIORITY_OFF;
+ }
return mService.getPriority(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (mService == null) return;
+ if (mService == null) {
+ return;
+ }
if (preferred) {
if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -169,7 +162,9 @@
}
public List<BluetoothDevice> getConnectedDevices() {
- if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
return mService.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
@@ -207,11 +202,11 @@
}
protected void finalize() {
- if (V) Log.d(TAG, "finalize()");
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP,
- mService);
+ mService);
mService = null;
}catch (Throwable t) {
Log.w(TAG, "Error cleaning up SAP proxy", t);
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
index 42306f6..9db4a35 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -46,7 +46,7 @@
* TODO: Remove duplicate codes once backward support ends.
*/
class LicenseHtmlGeneratorFromXml {
- private static final String TAG = "LicenseHtmlGeneratorFromXml";
+ private static final String TAG = "LicenseGeneratorFromXml";
private static final String TAG_ROOT = "licenses";
private static final String TAG_FILE_NAME = "file-name";
@@ -107,12 +107,13 @@
mXmlFiles = xmlFiles;
}
- public static boolean generateHtml(List<File> xmlFiles, File outputFile) {
+ public static boolean generateHtml(List<File> xmlFiles, File outputFile,
+ String noticeHeader) {
LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles);
- return genertor.generateHtml(outputFile);
+ return genertor.generateHtml(outputFile, noticeHeader);
}
- private boolean generateHtml(File outputFile) {
+ private boolean generateHtml(File outputFile, String noticeHeader) {
for (File xmlFile : mXmlFiles) {
parse(xmlFile);
}
@@ -125,7 +126,8 @@
try {
writer = new PrintWriter(outputFile);
- generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer);
+ generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer,
+ noticeHeader);
writer.flush();
writer.close();
@@ -239,13 +241,18 @@
@VisibleForTesting
static void generateHtml(Map<String, String> fileNameToContentIdMap,
- Map<String, String> contentIdToFileContentMap, PrintWriter writer) {
+ Map<String, String> contentIdToFileContentMap, PrintWriter writer,
+ String noticeHeader) {
List<String> fileNameList = new ArrayList();
fileNameList.addAll(fileNameToContentIdMap.keySet());
Collections.sort(fileNameList);
writer.println(HTML_HEAD_STRING);
+ if (!TextUtils.isEmpty(noticeHeader)) {
+ writer.println(noticeHeader);
+ }
+
int count = 0;
Map<String, Integer> contentIdToOrderMap = new HashMap();
List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
index 3930069..78e807c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -60,7 +60,7 @@
File cachedHtmlFile = getCachedHtmlFile(mContext);
if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
- || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+ || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) {
return cachedHtmlFile;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
index 360c19c..ca62485 100644
--- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.util.Log;
+import com.android.settingslib.R;
import com.android.settingslib.utils.AsyncLoaderCompat;
import java.io.File;
@@ -65,7 +66,7 @@
File cachedHtmlFile = getCachedHtmlFile(mContext);
if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
- || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+ || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) {
return cachedHtmlFile;
}
@@ -101,7 +102,8 @@
return outdated;
}
- static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
- return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+ static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
+ return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile,
+ context.getString(R.string.notice_header));
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
index 74bd97f..e9c5238 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java
@@ -37,7 +37,8 @@
/**
* Loader for historical chart data for both network and UID details.
*
- * Deprecated in favor of {@link NetworkCycleDataLoader}
+ * Deprecated in favor of {@link NetworkCycleChartDataLoader} and
+ * {@link NetworkCycleDataForUidLoader}
*
* @deprecated
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java
new file mode 100644
index 0000000..9b3ff8b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.net;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Usage data in a billing cycle with bucketized data for plotting the usage chart.
+ */
+public class NetworkCycleChartData extends NetworkCycleData {
+ public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1);
+
+ private List<NetworkCycleData> mUsageBuckets;
+
+ private NetworkCycleChartData() {
+ }
+
+ public List<NetworkCycleData> getUsageBuckets() {
+ return mUsageBuckets;
+ }
+
+ public static class Builder extends NetworkCycleData.Builder {
+ private NetworkCycleChartData mObject = new NetworkCycleChartData();
+
+ public Builder setUsageBuckets(List<NetworkCycleData> buckets) {
+ getObject().mUsageBuckets = buckets;
+ return this;
+ }
+
+ @Override
+ protected NetworkCycleChartData getObject() {
+ return mObject;
+ }
+
+ @Override
+ public NetworkCycleChartData build() {
+ return getObject();
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
new file mode 100644
index 0000000..7ae3398
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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.net;
+
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Loader for network data usage history. It returns a list of usage data per billing cycle with
+ * bucketized usages.
+ */
+public class NetworkCycleChartDataLoader
+ extends NetworkCycleDataLoader<List<NetworkCycleChartData>> {
+
+ private static final String TAG = "NetworkCycleChartLoader";
+
+ private final List<NetworkCycleChartData> mData;
+
+ private NetworkCycleChartDataLoader(Builder builder) {
+ super(builder);
+ mData = new ArrayList<NetworkCycleChartData>();
+ }
+
+ @Override
+ void recordUsage(long start, long end) {
+ try {
+ final NetworkStats stats = mNetworkStatsManager.querySummary(
+ mNetworkType, mSubId, start, end);
+ final long total = getTotalUsage(stats);
+ if (total > 0L) {
+ final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
+ builder.setUsageBuckets(getUsageBuckets(start, end))
+ .setStartTime(start)
+ .setEndTime(end)
+ .setTotalUsage(total);
+ mData.add(builder.build());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception querying network detail.", e);
+ }
+ }
+
+ @Override
+ List<NetworkCycleChartData> getCycleUsage() {
+ return mData;
+ }
+
+ public static Builder<?> builder(Context context) {
+ return new Builder<NetworkCycleChartDataLoader>(context) {
+ @Override
+ public NetworkCycleChartDataLoader build() {
+ return new NetworkCycleChartDataLoader(this);
+ }
+ };
+ }
+
+ private List<NetworkCycleData> getUsageBuckets(long start, long end) {
+ final List<NetworkCycleData> data = new ArrayList<>();
+ long bucketStart = start;
+ long bucketEnd = start + NetworkCycleChartData.BUCKET_DURATION_MS;
+ while (bucketEnd <= end) {
+ long usage = 0L;
+ try {
+ final NetworkStats stats = mNetworkStatsManager.querySummary(
+ mNetworkType, mSubId, bucketStart, bucketEnd);
+ usage = getTotalUsage(stats);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception querying network detail.", e);
+ }
+ data.add(new NetworkCycleData.Builder()
+ .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build());
+ bucketStart = bucketEnd;
+ bucketEnd += NetworkCycleChartData.BUCKET_DURATION_MS;
+ }
+ return data;
+ }
+
+ public static abstract class Builder<T extends NetworkCycleChartDataLoader>
+ extends NetworkCycleDataLoader.Builder<T> {
+
+ public Builder(Context context) {
+ super(context);
+ }
+
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java
index 2d8c0de..26c65a2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java
@@ -16,54 +16,55 @@
package com.android.settingslib.net;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
/**
- * Data structure representing usage data in a billing cycle.
+ * Base data structure representing usage data in a billing cycle.
*/
public class NetworkCycleData {
- public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1);
- public long startTime;
- public long endTime;
- public long totalUsage;
- public List<NetworkCycleData> usageBuckets;
- private NetworkCycleData(Builder builder) {
- startTime = builder.mStart;
- endTime = builder.mEnd;
- totalUsage = builder.mTotalUsage;
- usageBuckets = builder.mUsageBuckets;
+ private long mStartTime;
+ private long mEndTime;
+ private long mTotalUsage;
+
+ protected NetworkCycleData() {
+ }
+
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ public long getEndTime() {
+ return mEndTime;
+ }
+
+ public long getTotalUsage() {
+ return mTotalUsage;
}
public static class Builder {
- private long mStart;
- private long mEnd;
- private long mTotalUsage;
- private List<NetworkCycleData> mUsageBuckets;
+
+ private NetworkCycleData mObject = new NetworkCycleData();
public Builder setStartTime(long start) {
- mStart = start;
+ getObject().mStartTime = start;
return this;
}
public Builder setEndTime(long end) {
- mEnd = end;
+ getObject().mEndTime = end;
return this;
}
public Builder setTotalUsage(long total) {
- mTotalUsage = total;
+ getObject().mTotalUsage = total;
return this;
}
- public Builder setUsageBuckets(List<NetworkCycleData> buckets) {
- mUsageBuckets = buckets;
- return this;
+ protected NetworkCycleData getObject() {
+ return mObject;
}
public NetworkCycleData build() {
- return new NetworkCycleData(this);
+ return getObject();
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java
new file mode 100644
index 0000000..9d13717
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 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.net;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Usage data in a billing cycle for a specific Uid.
+ */
+public class NetworkCycleDataForUid extends NetworkCycleData {
+
+ private long mBackgroudUsage;
+ private long mForegroudUsage;
+
+ private NetworkCycleDataForUid() {
+ }
+
+ public long getBackgroudUsage() {
+ return mBackgroudUsage;
+ }
+
+ public long getForegroudUsage() {
+ return mForegroudUsage;
+ }
+
+ public static class Builder extends NetworkCycleData.Builder {
+
+ private NetworkCycleDataForUid mObject = new NetworkCycleDataForUid();
+
+ public Builder setBackgroundUsage(long backgroundUsage) {
+ getObject().mBackgroudUsage = backgroundUsage;
+ return this;
+ }
+
+ public Builder setForegroundUsage(long foregroundUsage) {
+ getObject().mForegroudUsage = foregroundUsage;
+ return this;
+ }
+
+ @Override
+ public NetworkCycleDataForUid getObject() {
+ return mObject;
+ }
+
+ @Override
+ public NetworkCycleDataForUid build() {
+ return getObject();
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
new file mode 100644
index 0000000..95efb4c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 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.net;
+
+import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+
+import android.app.usage.NetworkStats;
+import android.content.Context;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Loader for network data usage history. It returns a list of usage data per billing cycle for a
+ * specific Uid.
+ */
+public class NetworkCycleDataForUidLoader extends
+ NetworkCycleDataLoader<List<NetworkCycleDataForUid>> {
+ private static final String TAG = "NetworkDataForUidLoader";
+
+ private final List<NetworkCycleDataForUid> mData;
+ private final int mUid;
+
+ private NetworkCycleDataForUidLoader(Builder builder) {
+ super(builder);
+ mUid = builder.mUid;
+ mData = new ArrayList<NetworkCycleDataForUid>();
+ }
+
+ @Override
+ void recordUsage(long start, long end) {
+ try {
+ final NetworkStats stats = mNetworkStatsManager.queryDetailsForUid(
+ mNetworkType, mSubId, start, end, mUid);
+ final long total = getTotalUsage(stats);
+ if (total > 0L) {
+ final long foreground = getForegroundUsage(start, end);
+ final NetworkCycleDataForUid.Builder builder = new NetworkCycleDataForUid.Builder();
+ builder.setBackgroundUsage(total - foreground)
+ .setForegroundUsage(foreground)
+ .setStartTime(start)
+ .setEndTime(end)
+ .setTotalUsage(total);
+ mData.add(builder.build());
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exception querying network detail.", e);
+ }
+ }
+
+ @Override
+ List<NetworkCycleDataForUid> getCycleUsage() {
+ return mData;
+ }
+
+ public static Builder<?> builder(Context context) {
+ return new Builder<NetworkCycleDataForUidLoader>(context) {
+ @Override
+ public NetworkCycleDataForUidLoader build() {
+ return new NetworkCycleDataForUidLoader(this);
+ }
+ };
+ }
+
+ private long getForegroundUsage(long start, long end) {
+ final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState(
+ mNetworkType, mSubId, start, end, mUid, TAG_NONE, STATE_FOREGROUND);
+ return getTotalUsage(stats);
+ }
+
+ public static abstract class Builder<T extends NetworkCycleDataForUidLoader>
+ extends NetworkCycleDataLoader.Builder<T> {
+
+ private int mUid;
+
+ public Builder(Context context) {
+ super(context);
+ }
+
+ public Builder<T> setUid(int uid) {
+ mUid = uid;
+ return this;
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index 80e1356..cc936d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -32,32 +32,28 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.format.DateUtils;
-import android.util.Log;
import android.util.Pair;
import java.time.ZonedDateTime;
-import java.util.ArrayList;
import java.util.Iterator;
-import java.util.List;
-import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.loader.content.AsyncTaskLoader;
/**
* Loader for network data usage history. It returns a list of usage data per billing cycle.
*/
-public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleData>> {
- private static final String TAG = "CycleDataSummaryLoader";
- private final NetworkStatsManager mNetworkStatsManager;
- private final String mSubId;
- private final int mNetworkType;
+public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
+ private static final String TAG = "NetworkCycleDataLoader";
+ protected final NetworkStatsManager mNetworkStatsManager;
+ protected final String mSubId;
+ protected final int mNetworkType;
private final NetworkPolicy mPolicy;
private final NetworkTemplate mNetworkTemplate;
@VisibleForTesting
final INetworkStatsService mNetworkStatsService;
- private NetworkCycleDataLoader(Builder builder) {
+ protected NetworkCycleDataLoader(Builder<?> builder) {
super(builder.mContext);
mPolicy = builder.mPolicy;
mSubId = builder.mSubId;
@@ -75,21 +71,25 @@
forceLoad();
}
- @Override
- public List<NetworkCycleData> loadInBackground() {
+ public D loadInBackground() {
if (mPolicy == null) {
- return loadFourWeeksData();
+ loadFourWeeksData();
+ } else {
+ loadPolicyData();
}
- final List<NetworkCycleData> data = new ArrayList<>();
- final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator = NetworkPolicyManager
- .cycleIterator(mPolicy);
+ return getCycleUsage();
+ }
+
+ @VisibleForTesting
+ void loadPolicyData() {
+ final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator =
+ NetworkPolicyManager.cycleIterator(mPolicy);
while (iterator.hasNext()) {
final Pair<ZonedDateTime, ZonedDateTime> cycle = iterator.next();
final long cycleStart = cycle.first.toInstant().toEpochMilli();
final long cycleEnd = cycle.second.toInstant().toEpochMilli();
- getUsage(cycleStart, cycleEnd, data);
+ recordUsage(cycleStart, cycleEnd);
}
- return data;
}
@Override
@@ -105,8 +105,7 @@
}
@VisibleForTesting
- List<NetworkCycleData> loadFourWeeksData() {
- final List<NetworkCycleData> data = new ArrayList<>();
+ void loadFourWeeksData() {
try {
final INetworkStatsSession networkSession = mNetworkStatsService.openSession();
final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork(
@@ -117,7 +116,7 @@
long cycleEnd = historyEnd;
while (cycleEnd > historyStart) {
final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
- getUsage(cycleStart, cycleEnd, data);
+ recordUsage(cycleStart, cycleEnd);
cycleEnd = cycleStart;
}
@@ -125,29 +124,23 @@
} catch (RemoteException e) {
throw new RuntimeException(e);
}
- return data;
}
@VisibleForTesting
- void getUsage(long start, long end, @NonNull List<NetworkCycleData> data) {
- try {
- final NetworkStats stats = mNetworkStatsManager.querySummary(
- mNetworkType, mSubId, start, end);
- final long total = getTotalUsage(stats);
- if (total > 0L) {
- data.add(new NetworkCycleData.Builder()
- .setStartTime(start)
- .setEndTime(end)
- .setTotalUsage(total)
- .setUsageBuckets(getUsageBuckets(start, end))
- .build());
+ abstract void recordUsage(long start, long end);
+
+ abstract D getCycleUsage();
+
+ public static Builder<?> builder(Context context) {
+ return new Builder<NetworkCycleDataLoader>(context) {
+ @Override
+ public NetworkCycleDataLoader build() {
+ return null;
}
- } catch (RemoteException e) {
- Log.e(TAG, "Exception querying network detail.", e);
- }
+ };
}
- private long getTotalUsage(NetworkStats stats) {
+ protected long getTotalUsage(NetworkStats stats) {
long bytes = 0L;
if (stats != null) {
final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
@@ -159,61 +152,38 @@
return bytes;
}
- private List<NetworkCycleData> getUsageBuckets(long start, long end) {
- final List<NetworkCycleData> data = new ArrayList<>();
- long bucketStart = start;
- long bucketEnd = start + NetworkCycleData.BUCKET_DURATION_MS;
- while (bucketEnd <= end) {
- long usage = 0L;
- try {
- final NetworkStats stats = mNetworkStatsManager.querySummary(
- mNetworkType, mSubId, bucketStart, bucketEnd);
- usage = getTotalUsage(stats);
- } catch (RemoteException e) {
- Log.e(TAG, "Exception querying network detail.", e);
- }
- data.add(new NetworkCycleData.Builder()
- .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build());
- bucketStart = bucketEnd;
- bucketEnd += NetworkCycleData.BUCKET_DURATION_MS;
- }
- return data;
- }
-
- public static class Builder {
+ public static abstract class Builder<T extends NetworkCycleDataLoader> {
private final Context mContext;
private NetworkPolicy mPolicy;
private String mSubId;
private int mNetworkType;
private NetworkTemplate mNetworkTemplate;
- public Builder(Context context) {
+ public Builder (Context context) {
mContext = context;
}
- public Builder setNetworkPolicy(NetworkPolicy policy) {
+ public Builder<T> setNetworkPolicy(NetworkPolicy policy) {
mPolicy = policy;
return this;
}
- public Builder setSubscriberId(String subId) {
+ public Builder<T> setSubscriberId(String subId) {
mSubId = subId;
return this;
}
- public Builder setNetworkType(int networkType) {
+ public Builder<T> setNetworkType(int networkType) {
mNetworkType = networkType;
return this;
}
- public Builder setNetworkTemplate(NetworkTemplate template) {
+ public Builder<T> setNetworkTemplate(NetworkTemplate template) {
mNetworkTemplate = template;
return this;
}
- public NetworkCycleDataLoader build() {
- return new NetworkCycleDataLoader(this);
- }
+ public abstract T build();
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
new file mode 100644
index 0000000..9bb53ee
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSap;
+import android.bluetooth.BluetoothProfile;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
+
+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.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
+public class SapProfileTest {
+
+ @Mock
+ private CachedBluetoothDeviceManager mDeviceManager;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private BluetoothSap mService;
+ @Mock
+ private CachedBluetoothDevice mCachedBluetoothDevice;
+ @Mock
+ private BluetoothDevice mBluetoothDevice;
+ private BluetoothProfile.ServiceListener mServiceListener;
+ private SapProfile mProfile;
+ private ShadowBluetoothAdapter mShadowBluetoothAdapter;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ mProfile = new SapProfile(RuntimeEnvironment.application, mDeviceManager, mProfileManager);
+ mServiceListener = mShadowBluetoothAdapter.getServiceListener();
+ mServiceListener.onServiceConnected(BluetoothProfile.SAP, mService);
+ }
+
+ @Test
+ public void connect_shouldConnectBluetoothSap() {
+ mProfile.connect(mBluetoothDevice);
+ verify(mService).connect(mBluetoothDevice);
+ }
+
+ @Test
+ public void disconnect_shouldDisconnectBluetoothSap() {
+ mProfile.disconnect(mBluetoothDevice);
+ verify(mService).disconnect(mBluetoothDevice);
+ }
+
+ @Test
+ public void getConnectionStatus_shouldReturnConnectionState() {
+ when(mService.getConnectionState(mBluetoothDevice)).
+ thenReturn(BluetoothProfile.STATE_CONNECTED);
+ assertThat(mProfile.getConnectionStatus(mBluetoothDevice)).
+ isEqualTo(BluetoothProfile.STATE_CONNECTED);
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
index 96b2a14..b00476b2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -50,7 +50,7 @@
+ "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
+ "</licenses2>";
- private static final String EXPECTED_HTML_STRING =
+ private static final String HTML_HEAD_STRING =
"<html><head>\n"
+ "<style type=\"text/css\">\n"
+ "body { padding: 0; font-family: sans-serif; }\n"
@@ -63,8 +63,12 @@
+ "</head>"
+ "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
+ "<div class=\"toc\">\n"
- + "<ul>\n"
- + "<li><a href=\"#id0\">/file0</a></li>\n"
+ + "<ul>\n";
+
+ private static final String HTML_CUSTOM_HEADING = "Custom heading";
+
+ private static final String HTML_BODY_STRING =
+ "<li><a href=\"#id0\">/file0</a></li>\n"
+ "<li><a href=\"#id0\">/file1</a></li>\n"
+ "</ul>\n"
+ "</div><!-- table of contents -->\n"
@@ -81,6 +85,11 @@
+ "</td></tr><!-- same-license -->\n"
+ "</table></body></html>\n";
+ private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING;
+
+ private static final String EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING =
+ HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_BODY_STRING;
+
@Test
public void testParseValidXmlStream() throws XmlPullParserException, IOException {
Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
@@ -117,7 +126,23 @@
StringWriter output = new StringWriter();
LicenseHtmlGeneratorFromXml.generateHtml(
- fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output));
+ fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), "");
assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING);
}
+
+ @Test
+ public void testGenerateHtmlWithCustomHeading() {
+ Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+ Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+ fileNameToContentIdMap.put("/file0", "0");
+ fileNameToContentIdMap.put("/file1", "0");
+ contentIdToFileContentMap.put("0", "license content #0");
+
+ StringWriter output = new StringWriter();
+ LicenseHtmlGeneratorFromXml.generateHtml(
+ fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output),
+ HTML_CUSTOM_HEADING);
+ assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
index 12a4e69..c32cc99 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java
@@ -142,7 +142,7 @@
}
@Implementation
- static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+ static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) {
return sGenerateHtmlFileSucceeded;
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
new file mode 100644
index 0000000..3dc110d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 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.net;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.os.RemoteException;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class NetworkCycleChartDataLoaderTest {
+
+ @Mock
+ private NetworkStatsManager mNetworkStatsManager;
+ @Mock
+ private Context mContext;
+
+ private NetworkCycleChartDataLoader mLoader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+ .thenReturn(mNetworkStatsManager);
+ }
+
+ @Test
+ public void recordUsage_shouldQueryNetworkSummary() throws RemoteException {
+ final long end = System.currentTimeMillis();
+ final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
+ final int networkType = ConnectivityManager.TYPE_MOBILE;
+ final String subId = "TestSubscriber";
+ mLoader = NetworkCycleChartDataLoader.builder(mContext)
+ .setNetworkType(networkType).setSubscriberId(subId).build();
+
+ mLoader.recordUsage(start, end);
+
+ verify(mNetworkStatsManager).querySummary(networkType, subId, start, end);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
new file mode 100644
index 0000000..53fe451
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 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.net;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class NetworkCycleDataForUidLoaderTest {
+
+ @Mock
+ private NetworkStatsManager mNetworkStatsManager;
+ @Mock
+ private Context mContext;
+
+ private NetworkCycleDataForUidLoader mLoader;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
+ .thenReturn(mNetworkStatsManager);
+ }
+
+ @Test
+ public void recordUsage_shouldQueryNetworkDetailsForUid() {
+ final long end = System.currentTimeMillis();
+ final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
+ final int networkType = ConnectivityManager.TYPE_MOBILE;
+ final String subId = "TestSubscriber";
+ final int uid = 1;
+ mLoader = NetworkCycleDataForUidLoader.builder(mContext)
+ .setUid(uid).setNetworkType(networkType).setSubscriberId(subId).build();
+
+ mLoader.recordUsage(start, end);
+
+ verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index 4c4207b..be7f1bb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -16,13 +16,10 @@
package com.android.settingslib.net;
-import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.anyLong;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -50,6 +47,7 @@
import java.time.ZonedDateTime;
import java.util.Iterator;
+import java.util.List;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class NetworkCycleDataLoaderTest {
@@ -64,8 +62,11 @@
private Iterator<Range<ZonedDateTime>> mIterator;
@Mock
private INetworkStatsService mNetworkStatsService;
+ @Mock
+ private NetworkCycleDataLoader.Builder mBuilder;
- private NetworkCycleDataLoader mLoader;
+
+ private NetworkCycleDataTestLoader mLoader;
@Before
public void setUp() {
@@ -77,8 +78,8 @@
@Test
public void loadInBackground_noNetworkPolicy_shouldLoad4WeeksData() {
- mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build());
- doReturn(null).when(mLoader).loadFourWeeksData();
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+ doNothing().when(mLoader).loadFourWeeksData();
mLoader.loadInBackground();
@@ -86,31 +87,45 @@
}
@Test
- public void loadInBackground_shouldQueryNetworkSummary() throws RemoteException {
+ public void loadInBackground_hasNetworkPolicy_shouldLoadPolicyData() {
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+ ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy);
+
+ mLoader.loadInBackground();
+
+ verify(mLoader).loadPolicyData();
+ }
+
+ @Test
+ public void loadPolicyData_shouldRecordUsageFromPolicyCycle() {
final int networkType = ConnectivityManager.TYPE_MOBILE;
final String subId = "TestSubscriber";
final ZonedDateTime now = ZonedDateTime.now();
final Range<ZonedDateTime> cycle = new Range<>(now, now);
+ final long nowInMs = now.toInstant().toEpochMilli();
// mock 1 cycle data.
// hasNext() will be called internally in next(), hence setting it to return true twice.
when(mIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
when(mIterator.next()).thenReturn(cycle);
- mLoader = new NetworkCycleDataLoader.Builder(mContext)
- .setNetworkPolicy(mPolicy).setNetworkType(networkType).setSubscriberId(subId).build();
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
+ ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy);
+ ReflectionHelpers.setField(mLoader, "mNetworkType", networkType);
+ ReflectionHelpers.setField(mLoader, "mSubId", subId);
- mLoader.loadInBackground();
+ mLoader.loadPolicyData();
- verify(mNetworkStatsManager).querySummary(eq(networkType), eq(subId), anyLong(), anyLong());
+ verify(mLoader).recordUsage(nowInMs, nowInMs);
}
@Test
- public void loadFourWeeksData_shouldGetUsageForLast4Weeks() throws RemoteException {
- mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build());
+ public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() throws RemoteException {
+ mLoader = spy(new NetworkCycleDataTestLoader(mContext));
ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService);
final INetworkStatsSession networkSession = mock(INetworkStatsSession.class);
when(mNetworkStatsService.openSession()).thenReturn(networkSession);
final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class);
- when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt())).thenReturn(networkHistory);
+ when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt()))
+ .thenReturn(networkHistory);
final long now = System.currentTimeMillis();
final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4);
when(networkHistory.getStart()).thenReturn(fourWeeksAgo);
@@ -118,6 +133,23 @@
mLoader.loadFourWeeksData();
- verify(mLoader).getUsage(eq(fourWeeksAgo), eq(now), any());
+ verify(mLoader).recordUsage(fourWeeksAgo, now);
+ }
+
+ public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> {
+
+ private NetworkCycleDataTestLoader(Context context) {
+ super(NetworkCycleDataLoader.builder(mContext));
+ mContext = context;
+ }
+
+ @Override
+ void recordUsage(long start, long end) {
+ }
+
+ @Override
+ List<NetworkCycleData> getCycleUsage() {
+ return null;
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
deleted file mode 100644
index d8e73b7..0000000
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2018 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.testutils;
-
-import android.os.Bundle;
-import android.widget.LinearLayout;
-
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.fragment.app.FragmentManager;
-
-import org.robolectric.Robolectric;
-
-/**
- * Utilities for creating Fragments for testing.
- * <p>
- * TODO(b/111195449) - Duplicated from org.robolectric.shadows.support.v4.SupportFragmentTestUtil
- */
-@Deprecated
-public class FragmentTestUtils {
-
- public static void startFragment(Fragment fragment) {
- buildFragmentManager(FragmentUtilActivity.class)
- .beginTransaction().add(fragment, null).commit();
- }
-
- public static void startFragment(Fragment fragment,
- Class<? extends FragmentActivity> activityClass) {
- buildFragmentManager(activityClass)
- .beginTransaction().add(fragment, null).commit();
- }
-
- public static void startVisibleFragment(Fragment fragment) {
- buildFragmentManager(FragmentUtilActivity.class)
- .beginTransaction().add(1, fragment, null).commit();
- }
-
- public static void startVisibleFragment(Fragment fragment,
- Class<? extends FragmentActivity> activityClass, int containerViewId) {
- buildFragmentManager(activityClass)
- .beginTransaction().add(containerViewId, fragment, null).commit();
- }
-
- private static FragmentManager buildFragmentManager(
- Class<? extends FragmentActivity> activityClass) {
- FragmentActivity activity = Robolectric.setupActivity(activityClass);
- return activity.getSupportFragmentManager();
- }
-
- private static class FragmentUtilActivity extends FragmentActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- LinearLayout view = new LinearLayout(this);
- view.setId(1);
-
- setContentView(view);
- }
- }
-}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index bd21b83..c09b763 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -659,6 +659,9 @@
dumpSetting(s, p,
Settings.Global.GPU_DEBUG_LAYERS,
GlobalSettingsProto.Gpu.DEBUG_LAYERS);
+ dumpSetting(s, p,
+ Settings.Global.ANGLE_ENABLED_APP,
+ GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP);
p.end(gpuToken);
final long hdmiToken = p.start(GlobalSettingsProto.HDMI);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
index aa2fb32..814324e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java
@@ -20,6 +20,7 @@
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.annotations.ProvidesInterface;
+import java.io.PrintWriter;
@ProvidesInterface(action = NavGesture.ACTION, version = NavGesture.VERSION)
public interface NavGesture extends Plugin {
@@ -46,6 +47,8 @@
public void onNavigationButtonLongPress(View v);
public default void destroy() { }
+
+ public default void dump(PrintWriter pw) { }
}
}
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 710b5f7..defc49b 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -20,6 +20,10 @@
"src/**/I*.aidl",
],
+ static_libs: [
+ "SystemUIPluginLib"
+ ],
+
// Enforce that the library is build agains java 7 so that there are
// no compatibility issues with launcher
java_version: "1.7",
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
new file mode 100644
index 0000000..9857894
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.shared.plugins;
+
+import android.content.Context;
+import android.os.Looper;
+
+/**
+ * Provides necessary components for initializing {@link PluginManagerImpl}.
+ */
+public interface PluginInitializer {
+
+ Looper getBgLooper();
+
+ /**
+ * This Runnable is run on the bg looper during initialization of {@link PluginManagerImpl}.
+ * It can be null.
+ */
+ Runnable getBgInitCallback();
+
+ String[] getWhitelistedPlugins(Context context);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index 7bc7e5f..e80c079 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import android.app.Notification;
import android.app.Notification.Action;
@@ -39,12 +39,14 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginFragment;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import com.android.systemui.R;
public class PluginInstanceManager<T extends Plugin> {
@@ -71,8 +73,7 @@
PluginInstanceManager(Context context, String action, PluginListener<T> listener,
boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
- manager, Build.IS_DEBUGGABLE,
- context.getResources().getStringArray(R.array.config_pluginWhitelist));
+ manager, Build.IS_DEBUGGABLE, manager.getWhitelistedPlugins());
}
@VisibleForTesting
@@ -114,7 +115,7 @@
public void destroy() {
if (DEBUG) Log.d(TAG, "stopListening");
- ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (PluginInfo plugin : plugins) {
mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
plugin.mPlugin).sendToTarget();
@@ -132,7 +133,7 @@
public boolean checkAndDisable(String className) {
boolean disableAny = false;
- ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (PluginInfo info : plugins) {
if (className.startsWith(info.mPackage)) {
disable(info);
@@ -143,7 +144,7 @@
}
public boolean disableAll() {
- ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (int i = 0; i < plugins.size(); i++) {
disable(plugins.get(i));
}
@@ -165,7 +166,7 @@
}
public <T> boolean dependsOn(Plugin p, Class<T> cls) {
- ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
+ ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
for (PluginInfo info : plugins) {
if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) {
return info.mVersion != null && info.mVersion.hasClass(cls);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
index 298eaf1..208f4fe 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
@@ -12,10 +12,12 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import android.text.TextUtils;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.annotations.ProvidesInterface;
public interface PluginManager {
@@ -40,14 +42,17 @@
<T> boolean dependsOn(Plugin p, Class<T> cls);
- static <P> String getAction(Class<P> cls) {
- ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
- if (info == null) {
- throw new RuntimeException(cls + " doesn't provide an interface");
+ class Helper {
+ public static <P> String getAction(Class<P> cls) {
+ ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+ if (info == null) {
+ throw new RuntimeException(cls + " doesn't provide an interface");
+ }
+ if (TextUtils.isEmpty(info.action())) {
+ throw new RuntimeException(cls + " doesn't provide an action");
+ }
+ return info.action();
}
- if (TextUtils.isEmpty(info.action())) {
- throw new RuntimeException(cls + " doesn't provide an action");
- }
- return info.action();
}
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 1cbf1fe..7f1d161 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import android.app.Notification;
import android.app.Notification.Action;
@@ -41,10 +41,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
+
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import dalvik.system.PathClassLoader;
@@ -79,31 +80,33 @@
private Looper mLooper;
private boolean mWtfsSet;
- public PluginManagerImpl(Context context) {
+ public PluginManagerImpl(Context context, PluginInitializer initializer) {
this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
- context.getResources().getStringArray(R.array.config_pluginWhitelist),
- Thread.getUncaughtExceptionPreHandler());
+ Thread.getUncaughtExceptionPreHandler(), initializer);
}
@VisibleForTesting
PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
- String[] whitelistedPlugins, UncaughtExceptionHandler defaultHandler) {
+ UncaughtExceptionHandler defaultHandler, PluginInitializer initializer) {
mContext = context;
mFactory = factory;
- mLooper = Dependency.get(Dependency.BG_LOOPER);
+ mLooper = initializer.getBgLooper();
isDebuggable = debuggable;
- mWhitelistedPlugins.addAll(Arrays.asList(whitelistedPlugins));
+ mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
mPluginPrefs = new PluginPrefs(mContext);
PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
defaultHandler);
Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler);
- new Handler(mLooper).post(() -> {
- // Plugin dependencies that don't have another good home can go here, but
- // dependencies that have better places to init can happen elsewhere.
- Dependency.get(PluginDependencyProvider.class)
- .allowPluginDependency(ActivityStarter.class);
- });
+
+ Runnable bgRunnable = initializer.getBgInitCallback();
+ if (bgRunnable != null) {
+ new Handler(mLooper).post(bgRunnable);
+ }
+ }
+
+ public String[] getWhitelistedPlugins() {
+ return mWhitelistedPlugins.toArray(new String[0]);
}
public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
@@ -121,7 +124,9 @@
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new RuntimeException("Must be called from UI thread");
}
- PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
+ // Passing null causes compiler to complain about incompatible (generic) types.
+ PluginListener<Plugin> dummy = null;
+ PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy,
false, mLooper, cls, this);
mPluginPrefs.addAction(action);
PluginInfo<T> info = p.getPlugin();
@@ -140,7 +145,7 @@
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
boolean allowMultiple) {
- addPluginListener(PluginManager.getAction(cls), listener, cls, allowMultiple);
+ addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple);
}
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
@@ -293,8 +298,12 @@
public void handleWtfs() {
if (!mWtfsSet) {
mWtfsSet = true;
- Log.setWtfHandler((tag, what, system) -> {
- throw new CrashWhilePluginActiveException(what);
+ Log.setWtfHandler(new Log.TerribleFailureHandler() {
+ @Override
+ public void onTerribleFailure(String tag, Log.TerribleFailure what,
+ boolean system) {
+ throw new CrashWhilePluginActiveException(what);
+ }
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
index 3671b3c..c0c5d70 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import android.content.Context;
import android.content.SharedPreferences;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java
rename to packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
index facfd98..bb845cd 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java
@@ -12,7 +12,9 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
+
+import android.util.ArrayMap;
import com.android.systemui.plugins.annotations.Dependencies;
import com.android.systemui.plugins.annotations.DependsOn;
@@ -20,7 +22,7 @@
import com.android.systemui.plugins.annotations.Requirements;
import com.android.systemui.plugins.annotations.Requires;
-import android.util.ArrayMap;
+import java.util.function.BiConsumer;
public class VersionInfo {
@@ -73,25 +75,32 @@
}
public void checkVersion(VersionInfo plugin) throws InvalidVersionException {
- ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions);
- plugin.mVersions.forEach((aClass, version) -> {
- Version v = versions.remove(aClass);
- if (v == null) {
- v = createVersion(aClass);
- }
- if (v == null) {
- throw new InvalidVersionException(aClass.getSimpleName()
- + " does not provide an interface", false);
- }
- if (v.mVersion != version.mVersion) {
- throw new InvalidVersionException(aClass, v.mVersion < version.mVersion, v.mVersion,
- version.mVersion);
+ final ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions);
+ plugin.mVersions.forEach(new BiConsumer<Class<?>, Version>() {
+ @Override
+ public void accept(Class<?> aClass, Version version) {
+ Version v = versions.remove(aClass);
+ if (v == null) {
+ v = VersionInfo.this.createVersion(aClass);
+ }
+ if (v == null) {
+ throw new InvalidVersionException(aClass.getSimpleName()
+ + " does not provide an interface", false);
+ }
+ if (v.mVersion != version.mVersion) {
+ throw new InvalidVersionException(aClass, v.mVersion < version.mVersion,
+ v.mVersion,
+ version.mVersion);
+ }
}
});
- versions.forEach((aClass, version) -> {
- if (version.mRequired) {
- throw new InvalidVersionException("Missing required dependency "
- + aClass.getSimpleName(), false);
+ versions.forEach(new BiConsumer<Class<?>, Version>() {
+ @Override
+ public void accept(Class<?> aClass, Version version) {
+ if (version.mRequired) {
+ throw new InvalidVersionException("Missing required dependency "
+ + aClass.getSimpleName(), false);
+ }
}
});
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 5bbbc52..28eff46 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -14,7 +14,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
/**
* Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index b2cf305..fe1fe1a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -39,9 +39,10 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginInitializerImpl;
import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.power.EnhancedEstimatesImpl;
@@ -236,7 +237,7 @@
new DeviceProvisionedControllerImpl(mContext));
mProviders.put(PluginManager.class, () ->
- new PluginManagerImpl(mContext));
+ new PluginManagerImpl(mContext, new PluginInitializerImpl()));
mProviders.put(AssistManager.class, () ->
new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 408e599..77f4bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -256,6 +256,12 @@
Log.d(TAG, "onSurfaceRedrawNeeded");
}
super.onSurfaceRedrawNeeded(holder);
+ // At the end of this method we should have drawn into the surface.
+ // This means that the bitmap should be loaded synchronously if
+ // it was already unloaded.
+ if (mBackground == null) {
+ updateBitmap(mWallpaperManager.getBitmap(true /* hardware */));
+ }
mSurfaceRedrawNeeded = true;
drawFrame();
}
diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
index ddd4833..f6ad626 100644
--- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
@@ -21,7 +21,7 @@
import android.view.View;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.ViewProvider;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index b96a604..78053b2 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -34,7 +34,7 @@
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.util.NotificationChannels;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index bb82a54..8e29841 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -28,8 +28,8 @@
import java.io.PrintWriter;
import com.android.internal.os.BinderInternal;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginManagerImpl;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManagerImpl;
public class SystemUIService extends Service {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index e6026c1..21b21d9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -44,7 +44,7 @@
public static final int PULSE_REASON_SENSOR_PICKUP = 3;
public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
- public static final int PULSE_REASON_SENSOR_REACH = 6;
+ public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 6;
public static final int REASON_SENSOR_WAKE_UP = 7;
private static boolean sRegisterKeyguardCallback = true;
@@ -177,9 +177,9 @@
log("state " + state);
}
- public static void traceReachWakeUp() {
+ public static void traceWakeLockScreenWakeUp() {
if (!ENABLED) return;
- log("reachWakeUp");
+ log("wakeLockScreenWakeUp");
}
public static void traceProximityResult(Context context, boolean near, long millis,
@@ -199,7 +199,7 @@
case PULSE_REASON_SENSOR_PICKUP: return "pickup";
case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
- case PULSE_REASON_SENSOR_REACH: return "reach";
+ case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakeLockScreen";
case REASON_SENSOR_WAKE_UP: return "wakeup";
default: throw new IllegalArgumentException("bad reason: " + pulseReason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index f9dfb5d..7013947 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -113,10 +113,10 @@
true /* reports touch coordinates */,
true /* touchscreen */),
new TriggerSensor(
- findSensorWithType(config.reachSensorType()),
- Settings.Secure.DOZE_REACH_GESTURE,
+ findSensorWithType(config.wakeLockScreenSensorType()),
+ Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE,
true /* configured */,
- DozeLog.PULSE_REASON_SENSOR_REACH,
+ DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN,
false /* reports touch coordinates */,
false /* touchscreen */),
new WakeScreenSensor(),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 7339304..c61e10a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -26,7 +26,7 @@
import com.android.systemui.plugins.DozeServicePlugin;
import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 31548b9..cb91d78 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -128,7 +128,7 @@
boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
- boolean isReach = pulseReason == DozeLog.PULSE_REASON_SENSOR_REACH;
+ boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN;
if (isLongPress) {
requestPulse(pulseReason, sensorPerformedProxCheck);
@@ -141,7 +141,7 @@
if (isDoubleTap) {
mDozeHost.onDoubleTap(screenX, screenY);
mMachine.wakeUp();
- } else if (isPickup || isReach) {
+ } else if (isPickup || isWakeLockScreen) {
mMachine.wakeUp();
} else {
mDozeHost.extendPulse();
@@ -156,8 +156,8 @@
final boolean withinVibrationThreshold =
timeSinceNotification < mDozeParameters.getPickupVibrationThreshold();
DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold);
- } else if (isReach) {
- DozeLog.traceReachWakeUp();
+ } else if (isWakeLockScreen) {
+ DozeLog.traceWakeLockScreenWakeUp();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
index c58d889..03daa95 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
@@ -18,6 +18,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.PluginDependency.DependencyProvider;
+import com.android.systemui.shared.plugins.PluginManager;
public class PluginDependencyProvider extends DependencyProvider {
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
new file mode 100644
index 0000000..108c2d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.content.Context;
+import android.os.Looper;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.shared.plugins.PluginInitializer;
+import com.android.systemui.R;
+
+public class PluginInitializerImpl implements PluginInitializer {
+ @Override
+ public Looper getBgLooper() {
+ return Dependency.get(Dependency.BG_LOOPER);
+ }
+
+ @Override
+ public Runnable getBgInitCallback() {
+ return new Runnable() {
+ @Override
+ public void run() {
+ // Plugin dependencies that don't have another good home can go here, but
+ // dependencies that have better places to init can happen elsewhere.
+ Dependency.get(PluginDependencyProvider.class)
+ .allowPluginDependency(ActivityStarter.class);
+ }
+ };
+ }
+
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return context.getResources().getStringArray(R.array.config_pluginWhitelist);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 8b2e1d5..e98ef4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -199,7 +199,11 @@
public void openDetails(String subPanel) {
QSTile tile = getTile(subPanel);
- showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
+ // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
+ // QSFactory will not be able to create a tile and getTile will return null
+ if (tile != null) {
+ showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
+ }
}
private QSTile getTile(String subPanel) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 86e69e3..cefeeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -31,7 +31,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.plugins.qs.QSTile;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9b1d334..bce613a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -72,7 +72,7 @@
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.statusbar.notification.NotificationData;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
deleted file mode 100644
index 62d2099..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.view.MotionEvent;
-import android.view.View;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-
-/**
- * TODO: Remove and replace with QuickStepController
- */
-public class NavigationBarGestureHelper implements GestureHelper {
-
- private static final String TAG = "NavigationBarGestureHelper";
-
- private NavigationBarView mNavigationBarView;
-
- private final QuickStepController mQuickStepController;
- private final StatusBar mStatusBar;
-
- public NavigationBarGestureHelper(Context context) {
- mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
- mQuickStepController = new QuickStepController(context);
- }
-
- public void setComponents(NavigationBarView navigationBarView) {
- mNavigationBarView = navigationBarView;
- mQuickStepController.setComponents(mNavigationBarView);
- }
-
- public void setBarState(boolean isVertical, boolean isRTL) {
- mQuickStepController.setBarState(isVertical, isRTL);
- }
-
- public boolean onInterceptTouchEvent(MotionEvent event) {
- if (!canHandleGestures()) {
- return false;
- }
- return mQuickStepController.onInterceptTouchEvent(event);
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- if (!canHandleGestures()) {
- return false;
- }
- return mQuickStepController.onTouchEvent(event);
- }
-
- public void onDraw(Canvas canvas) {
- mQuickStepController.onDraw(canvas);
- }
-
- public void onLayout(boolean changed, int left, int top, int right, int bottom) {
- mQuickStepController.onLayout(changed, left, top, right, bottom);
- }
-
- public void onDarkIntensityChange(float intensity) {
- mQuickStepController.onDarkIntensityChange(intensity);
- }
-
- public void onNavigationButtonLongPress(View v) {
- mQuickStepController.onNavigationButtonLongPress(v);
- }
-
- private boolean canHandleGestures() {
- return !mStatusBar.isKeyguardShowing();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index e6f2c33..52134d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -37,7 +37,7 @@
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider;
import com.android.systemui.statusbar.phone.ReverseLinearLayout.ReverseRelativeLayout;
import com.android.systemui.statusbar.policy.KeyButtonView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index e5c9100..16b2987 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -40,7 +40,6 @@
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
-import androidx.annotation.ColorInt;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -64,12 +63,11 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.phone.NavGesture;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsOnboarding;
-import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -314,8 +312,8 @@
public void setComponents(NotificationPanelView panel) {
mPanelView = panel;
- if (mGestureHelper instanceof NavigationBarGestureHelper) {
- ((NavigationBarGestureHelper) mGestureHelper).setComponents(this);
+ if (mGestureHelper instanceof QuickStepController) {
+ ((QuickStepController) mGestureHelper).setComponents(this);
}
}
@@ -1071,7 +1069,7 @@
@Override
public void onPluginDisconnected(NavGesture plugin) {
- NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext());
+ QuickStepController defaultHelper = new QuickStepController(getContext());
defaultHelper.setComponents(this);
if (mGestureHelper != null) {
mGestureHelper.destroy();
@@ -1117,6 +1115,7 @@
pw.println(" }");
mContextualButtonGroup.dump(pw);
+ mGestureHelper.dump(pw);
mRecentsOnboarding.dump(pw);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
index 9ff907b..9e561d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
@@ -24,7 +24,7 @@
import com.android.systemui.plugins.NotificationListenerController;
import com.android.systemui.plugins.NotificationListenerController.NotificationProvider;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index 30e6afa..bce52a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -58,10 +58,12 @@
import com.android.systemui.Interpolators;
import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.NavigationBarCompat;
+import java.io.PrintWriter;
/**
* Class to detect gestures on the navigation bar and implement quick scrub.
@@ -117,6 +119,7 @@
private final int mTrackEndPadding;
private final int mHomeBackGestureDragLimit;
private final Context mContext;
+ private final StatusBar mStatusBar;
private final Matrix mTransformGlobalMatrix = new Matrix();
private final Matrix mTransformLocalMatrix = new Matrix();
private final Paint mTrackPaint = new Paint();
@@ -195,6 +198,7 @@
public QuickStepController(Context context) {
final Resources res = context.getResources();
mContext = context;
+ mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness);
mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding);
@@ -218,6 +222,10 @@
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (mStatusBar.isKeyguardShowing()) {
+ // Disallow any handling when the keyguard is showing
+ return false;
+ }
return handleTouchEvent(event);
}
@@ -227,6 +235,11 @@
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (mStatusBar.isKeyguardShowing()) {
+ // Disallow any handling when the keyguard is showing
+ return false;
+ }
+
// The same down event was just sent on intercept and therefore can be ignored here
final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
&& mOverviewEventSender.getProxy() != null;
@@ -483,6 +496,21 @@
mHandler.removeCallbacksAndMessages(null);
}
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println("QuickStepController {");
+ pw.print(" "); pw.println("mQuickScrubActive=" + mQuickScrubActive);
+ pw.print(" "); pw.println("mQuickStepStarted=" + mQuickStepStarted);
+ pw.print(" "); pw.println("mAllowGestureDetection=" + mAllowGestureDetection);
+ pw.print(" "); pw.println("mBackGestureActive=" + mBackGestureActive);
+ pw.print(" "); pw.println("mCanPerformBack=" + mCanPerformBack);
+ pw.print(" "); pw.println("mNotificationsVisibleOnDown=" + mNotificationsVisibleOnDown);
+ pw.print(" "); pw.println("mIsVertical=" + mIsVertical);
+ pw.print(" "); pw.println("mIsRTL=" + mIsRTL);
+ pw.print(" "); pw.println("mIsInScreenPinning=" + mIsInScreenPinning);
+ pw.println("}");
+ }
+
private void startQuickStep(MotionEvent event) {
if (mIsInScreenPinning) {
mNavigationBarView.showPinningEscapeToast();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 226b645..c3b87af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2182,7 +2182,7 @@
}
@Override
- public void animateExpandSettingsPanel(String subPanel) {
+ public void animateExpandSettingsPanel(@Nullable String subPanel) {
if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible);
if (!panelsEnabled()) {
return;
@@ -2191,7 +2191,6 @@
// Settings are not available in setup
if (!mUserSetup) return;
-
if (subPanel != null) {
mQSPanel.openDetails(subPanel);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index 6d75cfc..a6146a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -22,7 +22,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -71,7 +71,7 @@
@Override
public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls) {
- return withPlugin(cls, PluginManager.getAction(cls));
+ return withPlugin(cls, PluginManager.Helper.getAction(cls));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index c294806..71414a2 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -34,11 +34,10 @@
import android.view.View;
import com.android.systemui.R;
-import com.android.systemui.plugins.PluginInstanceManager;
-import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.plugins.PluginPrefs;
+import com.android.systemui.shared.plugins.PluginInstanceManager;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginPrefs;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 088630f..5aa3035 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -32,7 +32,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
-import com.android.systemui.plugins.PluginPrefs;
+import com.android.systemui.shared.plugins.PluginPrefs;
public class TunerFragment extends PreferenceFragment {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 13c43f7..4810b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -18,6 +18,7 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
@@ -34,6 +35,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.ContentResolver;
@@ -129,6 +131,7 @@
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
private final KeyguardManager mKeyguard;
+ private final ActivityManager mActivityManager;
private final AccessibilityManagerWrapper mAccessibilityMgr;
private final Object mSafetyWarningLock = new Object();
private final Accessibility mAccessibility = new Accessibility();
@@ -154,6 +157,7 @@
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mController = Dependency.get(VolumeDialogController.class);
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mShowActiveStreamOnly = showActiveStreamOnly();
@@ -431,7 +435,9 @@
public void initSettingsH() {
if (mSettingsView != null) {
mSettingsView.setVisibility(
- mDeviceProvisionedController.isCurrentUserSetup() ? VISIBLE : GONE);
+ mDeviceProvisionedController.isCurrentUserSetup() &&
+ mActivityManager.getLockTaskModeState() == LOCK_TASK_MODE_NONE ?
+ VISIBLE : GONE);
}
if (mSettingsIcon != null) {
mSettingsIcon.setOnClickListener(v -> {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index e6e4857..62ca3f3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -40,7 +40,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 85cdfcc..12a122a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -14,8 +14,12 @@
package com.android.systemui.qs;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -23,15 +27,21 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.Collections;
@@ -41,19 +51,37 @@
public class QSPanelTest extends SysuiTestCase {
private MetricsLogger mMetricsLogger;
+ private TestableLooper mTestableLooper;
private QSPanel mQsPanel;
+ @Mock
private QSTileHost mHost;
+ @Mock
private QSCustomizer mCustomizer;
+ @Mock
+ private QSTile dndTile;
+ private ViewGroup mParentView;
+ @Mock
+ private QSDetail.Callback mCallback;
@Before
public void setup() throws Exception {
- TestableLooper.get(this).runWithLooper(() -> {
+ MockitoAnnotations.initMocks(this);
+
+ mTestableLooper = TestableLooper.get(this);
+ mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mQsPanel = new QSPanel(mContext, null);
- mHost = mock(QSTileHost.class);
+ // Provides a parent with non-zero size for QSPanel
+ mParentView = new FrameLayout(mContext);
+ mParentView.addView(mQsPanel);
+
+ when(dndTile.getTileSpec()).thenReturn("dnd");
when(mHost.getTiles()).thenReturn(Collections.emptyList());
- mCustomizer = mock(QSCustomizer.class);
+ when(mHost.createTileView(any(), anyBoolean())).thenReturn(mock(QSTileView.class));
+
mQsPanel.setHost(mHost, mCustomizer);
+ mQsPanel.addTile(dndTile, true);
+ mQsPanel.setCallback(mCallback);
});
}
@@ -64,4 +92,31 @@
mQsPanel.setExpanded(false);
verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
}
+
+ @Test
+ public void testOpenDetailsWithExistingTile_NoException() {
+ mTestableLooper.processAllMessages();
+ mQsPanel.openDetails("dnd");
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback).onShowingDetail(any(), anyInt(), anyInt());
+ }
+
+/* @Test
+ public void testOpenDetailsWithNullParameter_NoException() {
+ mTestableLooper.processAllMessages();
+ mQsPanel.openDetails(null);
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
+ }*/
+
+ @Test
+ public void testOpenDetailsWithNonExistingTile_NoException() {
+ mTestableLooper.processAllMessages();
+ mQsPanel.openDetails("invalid-name");
+ mTestableLooper.processAllMessages();
+
+ verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
index 19974f8..6d1ff8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -27,8 +27,10 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
import com.android.systemui.plugins.annotations.Requires;
import org.junit.After;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
similarity index 87%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
index 438f9e4..3c70205 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java
@@ -11,7 +11,7 @@
* KIND, either express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -35,9 +36,12 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
+import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginInitializerImpl;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
import org.junit.Before;
import org.junit.Test;
@@ -74,8 +78,14 @@
when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
.thenReturn(mMockPluginInstance);
- mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true, new String[0],
- mMockExceptionHandler);
+
+ mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[0];
+ }
+ });
resetExceptionHandler();
mMockListener = mock(PluginListener.class);
}
@@ -109,7 +119,12 @@
@RunWithLooper(setAsMainLooper = true)
public void testNonDebuggable_noWhitelist() {
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
- new String[0], mMockExceptionHandler);
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[0];
+ }
+ });
resetExceptionHandler();
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
@@ -121,7 +136,12 @@
@RunWithLooper(setAsMainLooper = true)
public void testNonDebuggable_whitelistedPkg() {
mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
- new String[] {WHITELISTED_PACKAGE}, mMockExceptionHandler);
+ mMockExceptionHandler, new PluginInitializerImpl() {
+ @Override
+ public String[] getWhitelistedPlugins(Context context) {
+ return new String[] {WHITELISTED_PACKAGE};
+ }
+ });
resetExceptionHandler();
mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
index 0b4d9b5..9bad78d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java
@@ -12,7 +12,7 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.plugins;
+package com.android.systemui.shared.plugins;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -20,7 +20,8 @@
import android.support.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.VersionInfo.InvalidVersionException;
+import com.android.systemui.plugins.OverlayPlugin;
+import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
import com.android.systemui.plugins.annotations.Requires;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.DetailAdapter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index b22a646..1cceefa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -33,7 +33,7 @@
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 0a83a89..5f54bce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -14,12 +14,11 @@
package com.android.systemui.utils.leaks;
-import android.content.Context;
import android.testing.LeakCheck;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
public class FakePluginManager implements PluginManager {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index ecda9620..f479126 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -20,7 +20,7 @@
import android.util.ArrayMap;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 90c10fd..3947295 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6447,7 +6447,7 @@
// OPEN: Settings > System > Input & Gesture > Reach up gesture
// OS: Q
- SETTINGS_GESTURE_REACH = 1557;
+ SETTINGS_GESTURE_WAKE_LOCK_SCREEN = 1557;
// OPEN: Emergency dialer opened
// CLOSE: Emergency dialer closed
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e41a09e..a1989e5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,6 +57,7 @@
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.IConnectivityManager;
import android.net.IIpConnectivityMetrics;
+import android.net.INetd;
import android.net.INetdEventCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
@@ -88,6 +89,7 @@
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.NetdService;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -259,7 +261,8 @@
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
- private INetworkManagementService mNetd;
+ private INetworkManagementService mNMS;
+ private INetd mNetd;
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
@@ -759,7 +762,7 @@
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
mContext = checkNotNull(context, "missing Context");
- mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+ mNMS = checkNotNull(netManager, "missing INetworkManagementService");
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
mPolicyManagerInternal = checkNotNull(
@@ -767,6 +770,7 @@
"missing NetworkPolicyManagerInternal");
mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
+ mNetd = NetdService.getInstance();
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -849,7 +853,7 @@
mTethering = makeTethering();
- mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
+ mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
//set up the listener for user state for creating user VPNs
IntentFilter intentFilter = new IntentFilter();
@@ -864,8 +868,8 @@
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
try {
- mNetd.registerObserver(mTethering);
- mNetd.registerObserver(mDataActivityObserver);
+ mNMS.registerObserver(mTethering);
+ mNMS.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -896,7 +900,7 @@
mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
- mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+ mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
registerPrivateDnsSettingsCallbacks();
}
@@ -912,7 +916,7 @@
return mDefaultRequest;
}
};
- return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+ return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties(),
deps);
}
@@ -1476,6 +1480,20 @@
};
/**
+ * Ensures that the system cannot call a particular method.
+ */
+ private boolean disallowedBecauseSystemCaller() {
+ // TODO: start throwing a SecurityException when GnssLocationProvider stops calling
+ // requestRouteToHost.
+ if (isSystem(Binder.getCallingUid())) {
+ log("This method exists only for app backwards compatibility"
+ + " and must not be called by system services.");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
* @param networkType the type of the network over which traffic to the
@@ -1486,6 +1504,9 @@
*/
@Override
public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+ if (disallowedBecauseSystemCaller()) {
+ return false;
+ }
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityInternalPermission();
@@ -1563,7 +1584,7 @@
if (DBG) log("Adding legacy route " + bestRoute +
" for UID/PID " + uid + "/" + Binder.getCallingPid());
try {
- mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
+ mNMS.addLegacyRouteForNetId(netId, bestRoute, uid);
} catch (Exception e) {
// never crash - catch them all
if (DBG) loge("Exception trying to add a route: " + e);
@@ -1853,7 +1874,7 @@
if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
try {
- mNetd.addIdleTimer(iface, timeout, type);
+ mNMS.addIdleTimer(iface, timeout, type);
} catch (Exception e) {
// You shall not crash!
loge("Exception in setupDataActivityTracking " + e);
@@ -1872,7 +1893,7 @@
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
try {
// the call fails silently if no idle timer setup for this interface
- mNetd.removeIdleTimer(iface);
+ mNMS.removeIdleTimer(iface);
} catch (Exception e) {
loge("Exception in removeDataActivityTracking " + e);
}
@@ -1880,6 +1901,18 @@
}
/**
+ * Update data activity tracking when network state is updated.
+ */
+ private void updateDataActivityTracking(NetworkAgentInfo newNetwork,
+ NetworkAgentInfo oldNetwork) {
+ if (newNetwork != null) {
+ setupDataActivityTracking(newNetwork);
+ }
+ if (oldNetwork != null) {
+ removeDataActivityTracking(oldNetwork);
+ }
+ }
+ /**
* Reads the network specific MTU size from resources.
* and set it on it's iface.
*/
@@ -1907,7 +1940,7 @@
try {
if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
- mNetd.setMtu(iface, mtu);
+ mNMS.setMtu(iface, mtu);
} catch (Exception e) {
Slog.e(TAG, "exception in setMtu()" + e);
}
@@ -2561,7 +2594,7 @@
}
nai.clearLingerState();
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- removeDataActivityTracking(nai);
+ updateDataActivityTracking(null /* newNetwork */, nai);
notifyLockdownVpn(nai);
ensureNetworkTransitionWakelock(nai.name());
}
@@ -2581,7 +2614,7 @@
// NetworkFactories, so network traffic isn't interrupted for an unnecessarily
// long time.
try {
- mNetd.removeNetwork(nai.network.netId);
+ mNMS.removeNetwork(nai.network.netId);
} catch (Exception e) {
loge("Exception removing network: " + e);
}
@@ -2779,20 +2812,6 @@
}
}
- // TODO: remove this code once we know that the Slog.wtf is never hit.
- //
- // Find all networks that are satisfying this request and remove the request
- // from their request lists.
- // TODO - it's my understanding that for a request there is only a single
- // network satisfying it, so this loop is wasteful
- for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) {
- if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) {
- Slog.wtf(TAG, "Request " + nri.request + " satisfied by " +
- otherNai.name() + ", but mNetworkAgentInfos says " +
- (nai != null ? nai.name() : "null"));
- }
- }
-
// Maintain the illusion. When this request arrived, we might have pretended
// that a network connected to serve it, even though the network was already
// connected. Now that this request has gone away, we might have to pretend
@@ -3760,7 +3779,7 @@
Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
- setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile));
} else {
setLockdownTracker(null);
}
@@ -4015,7 +4034,7 @@
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
updateLockdownVpn();
@@ -4632,7 +4651,7 @@
mDnsManager.updatePrivateDnsStatus(netId, newLp);
// Start or stop clat accordingly to network state.
- networkAgent.updateClat(mNetd);
+ networkAgent.updateClat(mNMS);
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
@@ -4671,9 +4690,9 @@
final String prefix = "iface:" + iface;
try {
if (add) {
- mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask);
+ mNetd.wakeupAddInterface(iface, prefix, mark, mask);
} else {
- mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask);
+ mNetd.wakeupDelInterface(iface, prefix, mark, mask);
}
} catch (Exception e) {
loge("Exception modifying wakeup packet monitoring: " + e);
@@ -4689,7 +4708,7 @@
for (String iface : interfaceDiff.added) {
try {
if (DBG) log("Adding iface " + iface + " to network " + netId);
- mNetd.addInterfaceToNetwork(iface, netId);
+ mNMS.addInterfaceToNetwork(iface, netId);
wakeupModifyInterface(iface, caps, true);
} catch (Exception e) {
loge("Exception adding interface: " + e);
@@ -4699,7 +4718,7 @@
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
wakeupModifyInterface(iface, caps, false);
- mNetd.removeInterfaceFromNetwork(iface, netId);
+ mNMS.removeInterfaceFromNetwork(iface, netId);
} catch (Exception e) {
loge("Exception removing interface: " + e);
}
@@ -4723,7 +4742,7 @@
if (route.hasGateway()) continue;
if (VDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNetd.addRoute(netId, route);
+ mNMS.addRoute(netId, route);
} catch (Exception e) {
if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
loge("Exception in addRoute for non-gateway: " + e);
@@ -4734,7 +4753,7 @@
if (route.hasGateway() == false) continue;
if (VDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNetd.addRoute(netId, route);
+ mNMS.addRoute(netId, route);
} catch (Exception e) {
if ((route.getGateway() instanceof Inet4Address) || VDBG) {
loge("Exception in addRoute for gateway: " + e);
@@ -4745,7 +4764,7 @@
for (RouteInfo route : routeDiff.removed) {
if (VDBG) log("Removing Route [" + route + "] from network " + netId);
try {
- mNetd.removeRoute(netId, route);
+ mNMS.removeRoute(netId, route);
} catch (Exception e) {
loge("Exception in removeRoute: " + e);
}
@@ -4857,7 +4876,7 @@
final String newPermission = getNetworkPermission(newNc);
if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
try {
- mNetd.setNetworkPermission(nai.network.netId, newPermission);
+ mNMS.setNetworkPermission(nai.network.netId, newPermission);
} catch (RemoteException e) {
loge("Exception in setNetworkPermission: " + e);
}
@@ -4917,12 +4936,12 @@
if (!newRanges.isEmpty()) {
final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
newRanges.toArray(addedRangesArray);
- mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray);
+ mNMS.addVpnUidRanges(nai.network.netId, addedRangesArray);
}
if (!prevRanges.isEmpty()) {
final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
prevRanges.toArray(removedRangesArray);
- mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray);
+ mNMS.removeVpnUidRanges(nai.network.netId, removedRangesArray);
}
} catch (Exception e) {
// Never crash!
@@ -5091,9 +5110,9 @@
private void makeDefault(NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
- setupDataActivityTracking(newNetwork);
+
try {
- mNetd.setDefaultNetId(newNetwork.network.netId);
+ mNMS.setDefaultNetId(newNetwork.network.netId);
} catch (Exception e) {
loge("Exception setting default network :" + e);
}
@@ -5266,6 +5285,7 @@
}
}
if (isNewDefault) {
+ updateDataActivityTracking(newNetwork, oldDefaultNetwork);
// Notify system services that this network is up.
makeDefault(newNetwork);
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
@@ -5488,12 +5508,12 @@
try {
// This should never fail. Specifying an already in use NetID will cause failure.
if (networkAgent.isVPN()) {
- mNetd.createVirtualNetwork(networkAgent.network.netId,
+ mNMS.createVirtualNetwork(networkAgent.network.netId,
!networkAgent.linkProperties.getDnsServers().isEmpty(),
(networkAgent.networkMisc == null ||
!networkAgent.networkMisc.allowBypass));
} else {
- mNetd.createPhysicalNetwork(networkAgent.network.netId,
+ mNMS.createPhysicalNetwork(networkAgent.network.netId,
getNetworkPermission(networkAgent.networkCapabilities));
}
} catch (Exception e) {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1d163ee..de930f7 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -161,6 +161,8 @@
private static final int MAX_UID_RANGES_PER_COMMAND = 10;
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
/**
* Name representing {@link #setGlobalAlert(long)} limit when delivered to
* {@link INetworkManagementEventObserver#limitReached(String, String)}.
@@ -1234,18 +1236,12 @@
@Override
public void startTethering(String[] dhcpRange) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- // cmd is "tether start first_start first_stop second_start second_stop ..."
// an odd number of addrs will fail
- final Command cmd = new Command("tether", "start");
- for (String d : dhcpRange) {
- cmd.appendArg(d);
- }
-
try {
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherStart(dhcpRange);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1253,9 +1249,9 @@
public void stopTethering() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("tether", "stop");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherStop();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1263,25 +1259,21 @@
public boolean isTetheringStarted() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- final NativeDaemonEvent event;
try {
- event = mConnector.execute("tether", "status");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ final boolean isEnabled = mNetdService.tetherIsEnabled();
+ return isEnabled;
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
-
- // 210 Tethering services started
- event.checkCode(TetherStatusResult);
- return event.getMessage().endsWith("started");
}
@Override
public void tetherInterface(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("tether", "interface", "add", iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherInterfaceAdd(iface);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
List<RouteInfo> routes = new ArrayList<>();
// The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
@@ -1294,9 +1286,9 @@
public void untetherInterface(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- mConnector.execute("tether", "interface", "remove", iface);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherInterfaceRemove(iface);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
} finally {
removeInterfaceFromLocalNetwork(iface);
}
@@ -1306,11 +1298,10 @@
public String[] listTetheredInterfaces() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("tether", "interface", "list"),
- TetherInterfaceListResult);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ final List<String> result = mNetdService.tetherInterfaceList();
+ return result.toArray(EMPTY_STRING_ARRAY);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1319,16 +1310,11 @@
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET;
- final Command cmd = new Command("tether", "dns", "set", netId);
-
- for (String s : dns) {
- cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
- }
try {
- mConnector.execute(cmd);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ mNetdService.tetherDnsSet(netId, dns);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
@@ -1336,10 +1322,10 @@
public String[] getDnsForwarders() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
- return NativeDaemonEvent.filterMessageList(
- mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ final List<String> result = mNetdService.tetherDnsList();
+ return result.toArray(EMPTY_STRING_ARRAY);
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException(e);
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d505a77..21f54dd 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -329,6 +329,12 @@
@GuardedBy("mPackagesLock")
private final SparseArray<String> mSandboxIds = new SparseArray<>();
+ /**
+ * List of volumes visible to any user.
+ * TODO: may be have a map of userId -> volumes?
+ */
+ private final CopyOnWriteArrayList<VolumeInfo> mVisibleVols = new CopyOnWriteArrayList<>();
+
private volatile int mCurrentUserId = UserHandle.USER_SYSTEM;
/** Holding lock for AppFuse business */
@@ -623,16 +629,12 @@
Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy");
break;
}
- try {
- mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
- } catch (Exception e) {
- Slog.wtf(TAG, e);
- }
+ mount(vol);
break;
}
case H_VOLUME_UNMOUNT: {
final VolumeInfo vol = (VolumeInfo) msg.obj;
- unmount(vol.getId());
+ unmount(vol);
break;
}
case H_VOLUME_BROADCAST: {
@@ -869,6 +871,8 @@
addInternalVolumeLocked();
}
+ mVisibleVols.clear();
+
try {
mVold.reset();
@@ -1466,7 +1470,7 @@
= mContext.getPackageManager().getInstalledApplicationsAsUser(
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
synchronized (mPackagesLock) {
- final ArraySet<String> userPackages = getPackagesForUserPL(userId);
+ final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
for (int i = appInfos.size() - 1; i >= 0; --i) {
if (appInfos.get(i).isInstantApp()) {
continue;
@@ -1523,7 +1527,7 @@
}
@GuardedBy("mPackagesLock")
- private ArraySet<String> getPackagesForUserPL(int userId) {
+ private ArraySet<String> getAvailablePackagesForUserPL(int userId) {
ArraySet<String> userPackages = mPackages.get(userId);
if (userPackages == null) {
userPackages = new ArraySet<>();
@@ -1535,8 +1539,24 @@
private String[] getPackagesArrayForUser(int userId) {
if (!ENABLE_ISOLATED_STORAGE) return EmptyArray.STRING;
+ final ArraySet<String> userPackages;
synchronized (mPackagesLock) {
- return getPackagesForUserPL(userId).toArray(new String[0]);
+ userPackages = getAvailablePackagesForUserPL(userId);
+ if (!userPackages.isEmpty()) {
+ return userPackages.toArray(new String[0]);
+ }
+ }
+ final List<ApplicationInfo> appInfos =
+ mContext.getPackageManager().getInstalledApplicationsAsUser(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ synchronized (mPackagesLock) {
+ for (int i = appInfos.size() - 1; i >= 0; --i) {
+ if (appInfos.get(i).isInstantApp()) {
+ continue;
+ }
+ userPackages.add(appInfos.get(i).packageName);
+ }
+ return userPackages.toArray(new String[0]);
}
}
@@ -1747,8 +1767,15 @@
if (isMountDisallowed(vol)) {
throw new SecurityException("Mounting " + volId + " restricted by policy");
}
+ mount(vol);
+ }
+
+ private void mount(VolumeInfo vol) {
try {
mVold.mount(vol.id, vol.mountFlags, vol.mountUserId);
+ if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
+ mVisibleVols.add(vol);
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -1759,8 +1786,15 @@
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
final VolumeInfo vol = findVolumeByIdOrThrow(volId);
+ unmount(vol);
+ }
+
+ private void unmount(VolumeInfo vol) {
try {
mVold.unmount(vol.id);
+ if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) {
+ mVisibleVols.remove(vol);
+ }
} catch (Exception e) {
Slog.wtf(TAG, e);
}
@@ -3596,6 +3630,14 @@
pw.decreaseIndent();
pw.println();
+ pw.println("mVisibleVols:");
+ pw.increaseIndent();
+ for (int i = 0; i < mVisibleVols.size(); i++) {
+ mVisibleVols.get(i).dump(pw);
+ }
+ pw.decreaseIndent();
+
+ pw.println();
pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
final Pair<String, Long> pair = StorageManager.getPrimaryStoragePathAndSize();
if (pair == null) {
@@ -3716,7 +3758,7 @@
int userId) {
final String sandboxId;
synchronized (mPackagesLock) {
- final ArraySet<String> userPackages = getPackagesForUserPL(userId);
+ final ArraySet<String> userPackages = getAvailablePackagesForUserPL(userId);
// If userPackages is empty, it means the user is not started yet, so no need to
// do anything now.
if (userPackages.isEmpty() || userPackages.contains(packageName)) {
@@ -3734,5 +3776,29 @@
Slog.wtf(TAG, e);
}
}
+
+ @Override
+ public String[] getVisibleVolumesForUser(int userId) {
+ final ArrayList<String> visibleVolsForUser = new ArrayList<>();
+ for (int i = mVisibleVols.size() - 1; i >= 0; --i) {
+ final VolumeInfo vol = mVisibleVols.get(i);
+ if (vol.isVisibleForUser(userId)) {
+ visibleVolsForUser.add(getVolumeLabel(vol));
+ }
+ }
+ return visibleVolsForUser.toArray(new String[visibleVolsForUser.size()]);
+ }
+
+ private String getVolumeLabel(VolumeInfo vol) {
+ // STOPSHIP: Label needs to part of VolumeInfo and need to be passed on from vold
+ switch (vol.getType()) {
+ case VolumeInfo.TYPE_EMULATED:
+ return "emulated";
+ case VolumeInfo.TYPE_PUBLIC:
+ return vol.fsUuid == null ? vol.id : vol.fsUuid;
+ default:
+ return null;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 6d69fcd..0b836f0 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -94,7 +94,7 @@
"media.metrics", // system/bin/mediametrics
"media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
"com.android.bluetooth", // Bluetooth service
- "statsd", // Stats daemon
+ "/system/bin/statsd", // Stats daemon
};
public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2fd699e..a3a5ac8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -368,7 +368,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
-import java.lang.ref.WeakReference;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -2398,6 +2397,9 @@
mUserController = new UserController(this);
+ mPendingIntentController = new PendingIntentController(
+ mHandlerThread.getLooper(), mUserController);
+
GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
@@ -2413,9 +2415,6 @@
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mStackSupervisor = mActivityTaskManager.mStackSupervisor;
- mPendingIntentController = new PendingIntentController(
- mHandlerThread.getLooper(), mUserController);
-
mProcessCpuThread = new Thread("CpuTracker") {
@Override
public void run() {
@@ -3538,6 +3537,9 @@
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
try {
+ final String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+ final String[] visibleVolIds = LocalServices.getService(StorageManagerInternal.class)
+ .getVisibleVolumesForUser(UserHandle.getUserId(uid));
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
@@ -3547,12 +3549,14 @@
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, app.info.packageName,
+ packageNames, visibleVolIds,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, app.info.packageName,
+ packageNames, visibleVolIds,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
}
checkTime(startTime, "startProcess: returned from zygote!");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 66c7c43..f0ff570 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4685,7 +4685,9 @@
@Override
public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state)
{
- Log.i(TAG, "setBluetoothHearingAidDeviceConnectionState");
+ mDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "setHearingAidDeviceConnectionState state=" + state
+ + " addr=" + device.getAddress())).printLog(TAG));
setBluetoothHearingAidDeviceConnectionState(
device, state, false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE);
@@ -4723,12 +4725,12 @@
public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
int state, int profile, boolean suppressNoisyIntent, int a2dpVolume)
{
- mDeviceLogger.log(new AudioEventLogger.StringEvent(
+ mDeviceLogger.log((new AudioEventLogger.StringEvent(
"setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state
// only querying address as this is the only readily available field on the device
+ " addr=" + device.getAddress()
+ " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
- + " vol=" + a2dpVolume));
+ + " vol=" + a2dpVolume)).printLog(TAG));
if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
mDeviceLogger.log(new AudioEventLogger.StringEvent("A2DP connection state ignored"));
return 0;
@@ -5888,6 +5890,8 @@
}
private void onSendBecomingNoisyIntent() {
+ mDeviceLogger.log((new AudioEventLogger.StringEvent(
+ "broadcast ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG));
sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
}
@@ -7253,7 +7257,7 @@
// - wired: logged before onSetWiredDeviceConnectionState() is executed
// - A2DP: logged at reception of method call
final private AudioEventLogger mDeviceLogger = new AudioEventLogger(
- LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP device connection");
+ LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP/hearing aid device connection");
final private AudioEventLogger mForceUseLogger = new AudioEventLogger(
LOG_NB_EVENTS_FORCE_USE,
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 2a80f0e..48082b6 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -163,8 +163,8 @@
// TODO: create separate trackers for each unique VPN to support
// automated reconnection
- private Context mContext;
- private NetworkInfo mNetworkInfo;
+ private final Context mContext;
+ private final NetworkInfo mNetworkInfo;
private String mPackage;
private int mOwnerUID;
private String mInterface;
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 2b1d919..1e6bb04 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -320,9 +320,8 @@
}
private static boolean getEnableLegacyDhcpServer(Context ctx) {
- // TODO: make the default false (0) and update javadoc in Settings.java
final ContentResolver cr = ctx.getContentResolver();
- final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+ final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
return intVal != 0;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cade07c..9381fa2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4732,7 +4732,8 @@
if (notification.getSmallIcon() != null) {
StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
mListeners.notifyPostedLocked(r, old);
- if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
+ if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
+ && !isCritical(r)) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -4902,6 +4903,19 @@
}
/**
+ * Check if the notification is classified as critical.
+ *
+ * @param record the record to test for criticality
+ * @return {@code true} if notification is considered critical
+ *
+ * @see CriticalNotificationExtractor for criteria
+ */
+ private boolean isCritical(NotificationRecord record) {
+ // 0 is the most critical
+ return record.getCriticality() < CriticalNotificationExtractor.NORMAL;
+ }
+
+ /**
* Keeps the last 5 packages that have notified, by user.
*/
@GuardedBy("mNotificationLock")
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 10980b7..296d7ae 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8474,7 +8474,7 @@
private boolean canSkipFullApkVerification(String apkPath) {
final byte[] rootHashObserved;
try {
- rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath);
+ rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath);
if (rootHashObserved == null) {
return false; // APK does not contain Merkle tree root hash.
}
@@ -16010,7 +16010,8 @@
if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);
FileDescriptor fd = result.getUnownedFileDescriptor();
try {
- final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath);
+ final byte[] signedRootHash =
+ VerityUtils.generateApkVerityRootHash(apkPath);
mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
} catch (InstallerException | IOException | DigestException |
diff --git a/services/core/java/com/android/server/pm/dex/TEST_MAPPING b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
new file mode 100644
index 0000000..ad52559
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+ "presubmit": [
+ {
+ "name": "DexLoggerTests"
+ },
+ {
+ "name": "DexManagerTests"
+ },
+ {
+ "name": "DexoptOptionsTests"
+ },
+ {
+ "name": "DexoptUtilsTest"
+ },
+ {
+ "name": "PackageDexUsageTests"
+ },
+ {
+ "name": "DexLoggerIntegrationTests"
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
index 5e66bfc3..82d6b22 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java
@@ -135,7 +135,8 @@
final int userCount = other.mPermissionReviewRequired.size();
for (int i = 0; i < userCount; i++) {
final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i);
- mPermissionReviewRequired.put(i, reviewRequired);
+ mPermissionReviewRequired.put(other.mPermissionReviewRequired.keyAt(i),
+ reviewRequired);
}
}
}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 3796610..8070f3a 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -26,9 +26,9 @@
import android.util.Pair;
import android.util.Slog;
import android.util.apk.ApkSignatureVerifier;
-import android.util.apk.ApkVerityBuilder;
import android.util.apk.ByteBufferFactory;
import android.util.apk.SignatureNotFoundException;
+import android.util.apk.VerityBuilder;
import libcore.util.HexEncoding;
@@ -115,9 +115,9 @@
}
/**
- * {@see ApkSignatureVerifier#generateFsverityRootHash(String)}.
+ * {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}.
*/
- public static byte[] generateFsverityRootHash(@NonNull String apkPath)
+ public static byte[] generateApkVerityRootHash(@NonNull String apkPath)
throws NoSuchAlgorithmException, DigestException, IOException {
return ApkSignatureVerifier.generateApkVerityRootHash(apkPath);
}
@@ -146,7 +146,7 @@
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
- ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateFsVerityTree(
+ VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree(
file, trackedBufferFactory);
ByteBuffer buffer = result.verityData;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index c6e6449..3c64dd2 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -47,6 +47,7 @@
import android.os.IStoraged;
import android.os.IThermalEventListener;
import android.os.IThermalService;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
@@ -63,10 +64,13 @@
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.procstats.IProcessStats;
+import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.os.BinderCallsStats.ExportedCallStat;
import com.android.internal.os.KernelCpuSpeedReader;
@@ -82,6 +86,7 @@
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
@@ -95,6 +100,7 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -123,7 +129,7 @@
public static final String CONFIG_DIR = "/data/misc/stats-service";
static final String TAG = "StatsCompanionService";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
public static final int CODE_DATA_BROADCAST = 1;
public static final int CODE_SUBSCRIBER_BROADCAST = 1;
@@ -174,12 +180,14 @@
new KernelUidCpuClusterTimeReader();
private static IThermalService sThermalService;
+ private File mBaseDir =
+ new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
public StatsCompanionService(Context context) {
super();
mContext = context;
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-
+ mBaseDir.mkdirs();
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -1245,6 +1253,86 @@
Binder.restoreCallingIdentity(token);
}
+ long mLastProcStatsHighWaterMark = readProcStatsHighWaterMark();
+
+ private long readProcStatsHighWaterMark() {
+ try {
+ File[] files = mBaseDir.listFiles();
+ if (files == null || files.length == 0) {
+ return 0;
+ }
+ if (files.length > 1) {
+ Log.e(TAG, "Only 1 file expected for high water mark. Found " + files.length);
+ }
+ return Long.valueOf(files[0].getName());
+ } catch (SecurityException e) {
+ Log.e(TAG, "Failed to get procstats high watermark file.", e);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Failed to parse file name.", e);
+ }
+ return 0;
+ }
+
+ private IProcessStats mProcessStats =
+ IProcessStats.Stub.asInterface(ServiceManager.getService(ProcessStats.SERVICE_NAME));
+
+ private void pullProcessStats(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ try {
+ List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
+ long highWaterMark = mProcessStats.getCommittedStats(
+ mLastProcStatsHighWaterMark, ProcessStats.REPORT_ALL, true, statsFiles);
+ if (statsFiles.size() != 1) {
+ return;
+ }
+ InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0));
+ int[] len = new int[1];
+ byte[] stats = readFully(stream, len);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeStorage(Arrays.copyOf(stats, len[0]));
+ pulledData.add(e);
+ new File(mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).delete();
+ mLastProcStatsHighWaterMark = highWaterMark;
+ new File(
+ mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Getting procstats failed: ", e);
+ }
+ }
+
+ static byte[] readFully(InputStream stream, int[] outLen) throws IOException {
+ int pos = 0;
+ final int initialAvail = stream.available();
+ byte[] data = new byte[initialAvail > 0 ? (initialAvail + 1) : 16384];
+ while (true) {
+ int amt = stream.read(data, pos, data.length - pos);
+ if (DEBUG) {
+ Slog.i(TAG, "Read " + amt + " bytes at " + pos + " of avail " + data.length);
+ }
+ if (amt < 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "**** FINISHED READING: pos=" + pos + " len=" + data.length);
+ }
+ outLen[0] = pos;
+ return data;
+ }
+ pos += amt;
+ if (pos >= data.length) {
+ byte[] newData = new byte[pos + 16384];
+ if (DEBUG) {
+ Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length);
+ }
+ System.arraycopy(data, 0, newData, 0, pos);
+ data = newData;
+ }
+ }
+ }
+
/**
* Pulls various data.
*/
@@ -1358,6 +1446,10 @@
pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.PROC_STATS: {
+ pullProcessStats(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
@@ -1368,13 +1460,13 @@
@Override // Binder call
public void statsdReady() {
enforceCallingPermission();
- if (DEBUG) Slog.d(TAG, "learned that statsdReady");
+ if (DEBUG) {
+ Slog.d(TAG, "learned that statsdReady");
+ }
sayHiToStatsd(); // tell statsd that we're ready too and link to it
- mContext.sendBroadcastAsUser(
- new Intent(StatsManager.ACTION_STATSD_STARTED)
+ mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
- UserHandle.SYSTEM,
- android.Manifest.permission.DUMP);
+ UserHandle.SYSTEM, android.Manifest.permission.DUMP);
}
@Override
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 061f8e2..157b634 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -90,7 +90,6 @@
"libsensorservicehidl",
"libgui",
"libusbhost",
- "libsuspend",
"libtinyalsa",
"libEGL",
"libGLESv2",
@@ -122,6 +121,7 @@
"android.hardware.vr@1.0",
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
+ "android.system.suspend@1.0",
],
static_libs: [
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 02ad6c7..0ff60e4 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -30,6 +30,8 @@
#include <android/hardware/power/1.0/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
+#include <android/system/suspend/1.0/ISystemSuspend.h>
+#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
@@ -39,7 +41,6 @@
#include <log/log.h>
#include <utils/misc.h>
#include <utils/Log.h>
-#include <suspend/autosuspend.h>
using android::hardware::Return;
using android::hardware::Void;
@@ -49,6 +50,8 @@
using android::hardware::power::V1_1::PowerStateSubsystem;
using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
using android::hardware::hidl_vec;
+using android::system::suspend::V1_0::ISystemSuspend;
+using android::system::suspend::V1_0::ISystemSuspendCallback;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
@@ -63,6 +66,7 @@
extern sp<IPowerV1_0> getPowerHalV1_0();
extern sp<IPowerV1_1> getPowerHalV1_1();
extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
+extern sp<ISystemSuspend> getSuspendHal();
// Java methods used in getLowPowerStats
static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -70,16 +74,19 @@
static jmethodID jputVoter = NULL;
static jmethodID jputState = NULL;
-static void wakeup_callback(bool success)
-{
- ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
- int ret = sem_post(&wakeup_sem);
- if (ret < 0) {
- char buf[80];
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error posting wakeup sem: %s\n", buf);
+class WakeupCallback : public ISystemSuspendCallback {
+public:
+ Return<void> notifyWakeup(bool success) override {
+ ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+ int ret = sem_post(&wakeup_sem);
+ if (ret < 0) {
+ char buf[80];
+ strerror_r(errno, buf, sizeof(buf));
+ ALOGE("Error posting wakeup sem: %s\n", buf);
+ }
+ return Void();
}
-}
+};
static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
{
@@ -101,11 +108,14 @@
return -1;
}
ALOGV("Registering callback...");
- autosuspend_set_wakeup_callback(&wakeup_callback);
+ sp<ISystemSuspend> suspendHal = getSuspendHal();
+ suspendHal->registerCallback(new WakeupCallback());
}
// Wait for wakeup.
ALOGV("Waiting for wakeup...");
+ // TODO(b/116747600): device can suspend and wakeup after sem_wait() finishes and before wakeup
+ // reason is recorded, i.e. BatteryStats might occasionally miss wakeup events.
int ret = sem_wait(&wakeup_sem);
if (ret < 0) {
char buf[80];
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index b2d35d4..0c9b5f4 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -19,6 +19,7 @@
//#define LOG_NDEBUG 0
#include <android/hardware/power/1.1/IPower.h>
+#include <android/system/suspend/1.0/ISystemSuspend.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
@@ -35,7 +36,7 @@
#include <utils/Log.h>
#include <hardware/power.h>
#include <hardware_legacy/power.h>
-#include <suspend/autosuspend.h>
+#include <hidl/ServiceManagement.h>
#include "com_android_server_power_PowerManagerService.h"
@@ -44,6 +45,9 @@
using android::hardware::power::V1_0::PowerHint;
using android::hardware::power::V1_0::Feature;
using android::String8;
+using android::system::suspend::V1_0::ISystemSuspend;
+using android::system::suspend::V1_0::IWakeLock;
+using android::system::suspend::V1_0::WakeLockType;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
@@ -171,6 +175,46 @@
}
}
+static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<IWakeLock> gSuspendBlocker = nullptr;
+static std::mutex gSuspendMutex;
+
+// Assume SystemSuspend HAL is always alive.
+// TODO: Force device to restart if SystemSuspend HAL dies.
+sp<ISystemSuspend> getSuspendHal() {
+ static std::once_flag suspendHalFlag;
+ std::call_once(suspendHalFlag, [](){
+ ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default");
+ gSuspendHal = ISystemSuspend::getService();
+ assert(gSuspendHal != nullptr);
+ });
+ return gSuspendHal;
+}
+
+void enableAutoSuspend() {
+ static bool enabled = false;
+
+ std::lock_guard<std::mutex> lock(gSuspendMutex);
+ if (!enabled) {
+ sp<ISystemSuspend> suspendHal = getSuspendHal();
+ suspendHal->enableAutosuspend();
+ enabled = true;
+ }
+ if (gSuspendBlocker) {
+ gSuspendBlocker->release();
+ gSuspendBlocker.clear();
+ }
+}
+
+void disableAutoSuspend() {
+ std::lock_guard<std::mutex> lock(gSuspendMutex);
+ if (!gSuspendBlocker) {
+ sp<ISystemSuspend> suspendHal = getSuspendHal();
+ gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL,
+ "PowerManager.SuspendLockout");
+ }
+}
+
// ----------------------------------------------------------------------------
static void nativeInit(JNIEnv* env, jobject obj) {
@@ -207,13 +251,13 @@
static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) {
if (enable) {
android::base::Timer t;
- autosuspend_enable();
+ enableAutoSuspend();
if (t.duration() > 100ms) {
ALOGD("Excessive delay in autosuspend_enable() while turning screen off");
}
} else {
android::base::Timer t;
- autosuspend_disable();
+ disableAutoSuspend();
if (t.duration() > 100ms) {
ALOGD("Excessive delay in autosuspend_disable() while turning screen on");
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
new file mode 100644
index 0000000..5a787ec
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.server.usage;
+
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.fail;
+
+import static org.testng.Assert.assertEquals;
+
+import android.app.usage.EventList;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UsageStatsDatabaseTest {
+ protected Context mContext;
+ private UsageStatsDatabase mUsageStatsDatabase;
+ private File mTestDir;
+
+ private IntervalStats mIntervalStats = new IntervalStats();
+ private long mEndTime = 0;
+
+ private static final UsageStatsDatabase.StatCombiner<IntervalStats> mIntervalStatsVerifier =
+ new UsageStatsDatabase.StatCombiner<IntervalStats>() {
+ @Override
+ public void combine(IntervalStats stats, boolean mutable,
+ List<IntervalStats> accResult) {
+ accResult.add(stats);
+ }
+ };
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mTestDir = new File(mContext.getFilesDir(), "UsageStatsDatabaseTest");
+ mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+ mUsageStatsDatabase.init(1);
+ populateIntervalStats();
+ clearUsageStatsFiles();
+ }
+
+ /**
+ * A debugging utility for viewing the files currently in the test directory
+ */
+ private void clearUsageStatsFiles() {
+ File[] intervalDirs = mTestDir.listFiles();
+ for (File intervalDir : intervalDirs) {
+ if (intervalDir.isDirectory()) {
+ File[] usageFiles = intervalDir.listFiles();
+ for (File f : usageFiles) {
+ f.delete();
+ }
+ }
+ }
+ }
+
+ /**
+ * A debugging utility for viewing the files currently in the test directory
+ */
+ private String dumpUsageStatsFiles() {
+ StringBuilder sb = new StringBuilder();
+ File[] intervalDirs = mTestDir.listFiles();
+ for (File intervalDir : intervalDirs) {
+ if (intervalDir.isDirectory()) {
+ File[] usageFiles = intervalDir.listFiles();
+ for (File f : usageFiles) {
+ sb.append(f.toString());
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ private void populateIntervalStats() {
+ final int numberOfEvents = 3000;
+ long time = 1;
+ mIntervalStats = new IntervalStats();
+
+ mIntervalStats.beginTime = 1;
+ mIntervalStats.interactiveTracker.count = 2;
+ mIntervalStats.interactiveTracker.duration = 111111;
+ mIntervalStats.nonInteractiveTracker.count = 3;
+ mIntervalStats.nonInteractiveTracker.duration = 222222;
+ mIntervalStats.keyguardShownTracker.count = 4;
+ mIntervalStats.keyguardShownTracker.duration = 333333;
+ mIntervalStats.keyguardHiddenTracker.count = 5;
+ mIntervalStats.keyguardHiddenTracker.duration = 4444444;
+
+ if (mIntervalStats.events == null) {
+ mIntervalStats.events = new EventList();
+ }
+
+ for (int i = 0; i < numberOfEvents; i++) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ final int packageInt = ((i / 3) % 7);
+ event.mPackage = "fake.package.name" + packageInt; //clusters of 3 events from 7 "apps"
+ if (packageInt == 3) {
+ // Third app is an instant app
+ event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP;
+ } else if (packageInt == 2 || packageInt == 4) {
+ event.mClass = ".fake.class.name" + i % 11;
+ }
+
+
+ event.mTimeStamp = time;
+ event.mEventType = i % 19; //"random" event type
+
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ //empty config,
+ event.mConfiguration = new Configuration();
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ //"random" shortcut
+ event.mShortcutId = "shortcut" + (i % 8);
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ //"random" bucket and reason
+ event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8;
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ //"random" channel
+ event.mNotificationChannelId = "channel" + (i % 5);
+ break;
+ }
+
+ mIntervalStats.events.insert(event);
+ mIntervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+
+ time += 23; // Arbitrary progression of time
+ }
+ mEndTime = time;
+
+ Configuration config1 = new Configuration();
+ config1.fontScale = 3.3f;
+ config1.mcc = 4;
+ mIntervalStats.getOrCreateConfigurationStats(config1);
+
+ Configuration config2 = new Configuration();
+ config2.mnc = 5;
+ config2.setLocale(new Locale("en", "US"));
+ mIntervalStats.getOrCreateConfigurationStats(config2);
+
+ Configuration config3 = new Configuration();
+ config3.touchscreen = 6;
+ config3.keyboard = 7;
+ mIntervalStats.getOrCreateConfigurationStats(config3);
+
+ Configuration config4 = new Configuration();
+ config4.keyboardHidden = 8;
+ config4.hardKeyboardHidden = 9;
+ mIntervalStats.getOrCreateConfigurationStats(config4);
+
+ Configuration config5 = new Configuration();
+ config5.navigation = 10;
+ config5.navigationHidden = 11;
+ mIntervalStats.getOrCreateConfigurationStats(config5);
+
+ Configuration config6 = new Configuration();
+ config6.orientation = 12;
+ //Ignore screen layout, it's determined by locale
+ mIntervalStats.getOrCreateConfigurationStats(config6);
+
+ Configuration config7 = new Configuration();
+ config7.colorMode = 14;
+ config7.uiMode = 15;
+ mIntervalStats.getOrCreateConfigurationStats(config7);
+
+ Configuration config8 = new Configuration();
+ config8.screenWidthDp = 16;
+ config8.screenHeightDp = 17;
+ mIntervalStats.getOrCreateConfigurationStats(config8);
+
+ Configuration config9 = new Configuration();
+ config9.smallestScreenWidthDp = 18;
+ config9.densityDpi = 19;
+ mIntervalStats.getOrCreateConfigurationStats(config9);
+
+ mIntervalStats.activeConfiguration = config9;
+ }
+
+ void compareUsageStats(UsageStats us1, UsageStats us2) {
+ assertEquals(us1.mPackageName, us2.mPackageName);
+ // mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking
+ // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
+ assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+ assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
+ // mLaunchCount not persisted, so skipped
+ assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount);
+ assertEquals(us1.mLastEvent, us2.mLastEvent);
+ assertEquals(us1.mChooserCounts, us2.mChooserCounts);
+ }
+
+ void compareUsageEvent(UsageEvents.Event e1, UsageEvents.Event e2, int debugId) {
+ assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId);
+ assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId);
+ assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId);
+ assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId);
+ switch (e1.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ assertEquals(e1.mConfiguration, e2.mConfiguration,
+ "Usage event " + debugId + e2.mConfiguration.toString());
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId);
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ assertEquals(e1.mBucketAndReason, e2.mBucketAndReason, "Usage event " + debugId);
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId,
+ "Usage event " + debugId);
+ break;
+ }
+ assertEquals(e1.mFlags, e2.mFlags);
+ }
+
+ void compareIntervalStats(IntervalStats stats1, IntervalStats stats2) {
+ assertEquals(stats1.beginTime, stats2.beginTime);
+ assertEquals(stats1.endTime, stats2.endTime);
+ assertEquals(stats1.interactiveTracker.count, stats2.interactiveTracker.count);
+ assertEquals(stats1.interactiveTracker.duration, stats2.interactiveTracker.duration);
+ assertEquals(stats1.nonInteractiveTracker.count, stats2.nonInteractiveTracker.count);
+ assertEquals(stats1.nonInteractiveTracker.duration, stats2.nonInteractiveTracker.duration);
+ assertEquals(stats1.keyguardShownTracker.count, stats2.keyguardShownTracker.count);
+ assertEquals(stats1.keyguardShownTracker.duration, stats2.keyguardShownTracker.duration);
+ assertEquals(stats1.keyguardHiddenTracker.count, stats2.keyguardHiddenTracker.count);
+ assertEquals(stats1.keyguardHiddenTracker.duration, stats2.keyguardHiddenTracker.duration);
+
+ String[] usageKey1 = stats1.packageStats.keySet().toArray(new String[0]);
+ String[] usageKey2 = stats2.packageStats.keySet().toArray(new String[0]);
+ for (int i = 0; i < usageKey1.length; i++) {
+ UsageStats usageStats1 = stats1.packageStats.get(usageKey1[i]);
+ UsageStats usageStats2 = stats2.packageStats.get(usageKey2[i]);
+ compareUsageStats(usageStats1, usageStats2);
+ }
+
+ assertEquals(stats1.configurations.size(), stats2.configurations.size());
+ Configuration[] configSet1 = stats1.configurations.keySet().toArray(new Configuration[0]);
+ for (int i = 0; i < configSet1.length; i++) {
+ if (!stats2.configurations.containsKey(configSet1[i])) {
+ Configuration[] configSet2 = stats2.configurations.keySet().toArray(
+ new Configuration[0]);
+ String debugInfo = "";
+ for (Configuration c : configSet1) {
+ debugInfo += c.toString() + "\n";
+ }
+ debugInfo += "\n";
+ for (Configuration c : configSet2) {
+ debugInfo += c.toString() + "\n";
+ }
+ fail("Config " + configSet1[i].toString()
+ + " not found in deserialized IntervalStat\n" + debugInfo);
+ }
+ }
+ assertEquals(stats1.activeConfiguration, stats2.activeConfiguration);
+
+ assertEquals(stats1.events.size(), stats2.events.size());
+ for (int i = 0; i < stats1.events.size(); i++) {
+ compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i);
+ }
+ }
+
+ /**
+ * Runs the Write Read test.
+ * Will write the generated IntervalStat to disk, read it from disk and compare the two
+ */
+ void runWriteReadTest(int interval) throws IOException {
+ mUsageStatsDatabase.putUsageStats(interval, mIntervalStats);
+ List<IntervalStats> stats = mUsageStatsDatabase.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+
+ assertEquals(1, stats.size());
+ compareIntervalStats(mIntervalStats, stats.get(0));
+ }
+
+ /**
+ * Demonstrate that IntervalStats can be serialized and deserialized from disk without loss of
+ * relevant data.
+ */
+ @Test
+ public void testWriteRead() throws IOException {
+ runWriteReadTest(UsageStatsManager.INTERVAL_DAILY);
+ runWriteReadTest(UsageStatsManager.INTERVAL_WEEKLY);
+ runWriteReadTest(UsageStatsManager.INTERVAL_MONTHLY);
+ runWriteReadTest(UsageStatsManager.INTERVAL_YEARLY);
+ }
+
+ /**
+ * Runs the Version Change tests.
+ * Will write the generated IntervalStat to disk in one version format, "upgrade" to another
+ * version and read the automatically upgraded files on disk in the new file format.
+ */
+ void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
+ // Write IntervalStats to disk in old version format
+ UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
+ prevDB.init(1);
+ prevDB.putUsageStats(interval, mIntervalStats);
+
+ // Simulate an upgrade to a new version and read from the disk
+ UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, newVersion);
+ newDB.init(mEndTime);
+ List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+
+ assertEquals(1, stats.size());
+ // The written and read IntervalStats should match
+ compareIntervalStats(mIntervalStats, stats.get(0));
+ }
+
+ /**
+ * Test the version upgrade from 3 to 4
+ */
+ @Test
+ public void testVersionUpgradeFrom3to4() throws IOException {
+ runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_DAILY);
+ runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_WEEKLY);
+ runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_MONTHLY);
+ runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_YEARLY);
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 45d2fa2..b0d942c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2192,6 +2192,26 @@
}
@Test
+ public void testDontAutogroupIfCritical() throws Exception {
+ NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
+ r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW);
+ mService.addEnqueuedNotification(r);
+ NotificationManagerService.PostNotificationRunnable runnable =
+ mService.new PostNotificationRunnable(r.getKey());
+ runnable.run();
+
+ r = generateNotificationRecord(mTestNotificationChannel, 1, null, false);
+ r.setCriticality(CriticalNotificationExtractor.CRITICAL);
+ runnable = mService.new PostNotificationRunnable(r.getKey());
+ mService.addEnqueuedNotification(r);
+
+ runnable.run();
+ waitForIdle();
+
+ verify(mGroupHelper, never()).onNotificationPosted(any(), anyBoolean());
+ }
+
+ @Test
public void testNoFakeColorizedPermission() throws Exception {
when(mPackageManagerClient.checkPermission(any(), any())).thenReturn(PERMISSION_DENIED);
Notification.Builder nb = new Notification.Builder(mContext,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 702161e..13f3e5e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1030,6 +1030,14 @@
assertEquals(1, mZenModeHelperSpy.mConditions.mSubscriptions.size());
}
+ @Test
+ public void testEmptyDefaultRulesMap() {
+ ZenModeConfig config = new ZenModeConfig();
+ config.automaticRules = new ArrayMap<>();
+ mZenModeHelperSpy.mConfig = config;
+ mZenModeHelperSpy.updateDefaultZenRules(); // shouldn't throw null pointer
+ }
+
private void setupZenConfig() {
mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
mZenModeHelperSpy.mConfig.allowAlarms = false;
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 4b7e21f..db9972f 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -24,7 +24,9 @@
import android.content.res.Configuration;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.proto.ProtoInputStream;
+import java.io.IOException;
import java.util.List;
import com.android.internal.annotations.VisibleForTesting;
@@ -46,7 +48,7 @@
// keep hundreds of strings that have the same contents. We will read the string
// and only keep it if it's not in the cache. The GC will take care of the
// strings that had identical copies in the cache.
- private final ArraySet<String> mStringCache = new ArraySet<>();
+ public final ArraySet<String> mStringCache = new ArraySet<>();
public static final class EventTracker {
public long curStartTime;
@@ -129,6 +131,90 @@
return event;
}
+ /**
+ * Builds a UsageEvents.Event from a proto, but does not add it internally.
+ * Built here to take advantage of the cached String Refs
+ */
+ UsageEvents.Event buildEvent(ProtoInputStream parser, List<String> stringPool)
+ throws IOException {
+ final UsageEvents.Event event = new UsageEvents.Event();
+ while (true) {
+ switch (parser.nextField()) {
+ case (int) IntervalStatsProto.Event.PACKAGE:
+ event.mPackage = getCachedStringRef(
+ parser.readString(IntervalStatsProto.Event.PACKAGE));
+ break;
+ case (int) IntervalStatsProto.Event.PACKAGE_INDEX:
+ event.mPackage = getCachedStringRef(stringPool.get(
+ parser.readInt(IntervalStatsProto.Event.PACKAGE_INDEX) - 1));
+ break;
+ case (int) IntervalStatsProto.Event.CLASS:
+ event.mClass = getCachedStringRef(
+ parser.readString(IntervalStatsProto.Event.CLASS));
+ break;
+ case (int) IntervalStatsProto.Event.CLASS_INDEX:
+ event.mClass = getCachedStringRef(stringPool.get(
+ parser.readInt(IntervalStatsProto.Event.CLASS_INDEX) - 1));
+ break;
+ case (int) IntervalStatsProto.Event.TIME_MS:
+ event.mTimeStamp = beginTime + parser.readLong(
+ IntervalStatsProto.Event.TIME_MS);
+ break;
+ case (int) IntervalStatsProto.Event.FLAGS:
+ event.mFlags = parser.readInt(IntervalStatsProto.Event.FLAGS);
+ break;
+ case (int) IntervalStatsProto.Event.TYPE:
+ event.mEventType = parser.readInt(IntervalStatsProto.Event.TYPE);
+ break;
+ case (int) IntervalStatsProto.Event.CONFIG:
+ event.mConfiguration = new Configuration();
+ event.mConfiguration.readFromProto(parser, IntervalStatsProto.Event.CONFIG);
+ break;
+ case (int) IntervalStatsProto.Event.SHORTCUT_ID:
+ event.mShortcutId = parser.readString(
+ IntervalStatsProto.Event.SHORTCUT_ID).intern();
+ break;
+ case (int) IntervalStatsProto.Event.STANDBY_BUCKET:
+ event.mBucketAndReason = parser.readInt(
+ IntervalStatsProto.Event.STANDBY_BUCKET);
+ break;
+ case (int) IntervalStatsProto.Event.NOTIFICATION_CHANNEL:
+ event.mNotificationChannelId = parser.readString(
+ IntervalStatsProto.Event.NOTIFICATION_CHANNEL);
+ break;
+ case (int) IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX:
+ event.mNotificationChannelId = getCachedStringRef(stringPool.get(
+ parser.readInt(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX)
+ - 1));
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ // Handle default values for certain events types
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ if (event.mConfiguration == null) {
+ event.mConfiguration = new Configuration();
+ }
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ if (event.mShortcutId == null) {
+ event.mShortcutId = "";
+ }
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ if (event.mNotificationChannelId == null) {
+ event.mNotificationChannelId = "";
+ }
+ break;
+ }
+ if (event.mTimeStamp == 0) {
+ //mTimestamp not set, assume default value 0 plus beginTime
+ event.mTimeStamp = beginTime;
+ }
+ return event;
+ }
+ }
+ }
+
private boolean isStatefulEvent(int eventType) {
switch (eventType) {
case UsageEvents.Event.MOVE_TO_FOREGROUND:
@@ -143,8 +229,6 @@
/**
* Returns whether the event type is one caused by user visible
* interaction. Excludes those that are internally generated.
- * @param eventType
- * @return
*/
private boolean isUserVisibleEvent(int eventType) {
return eventType != UsageEvents.Event.SYSTEM_INTERACTION
@@ -184,6 +268,25 @@
endTime = timeStamp;
}
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public void addEvent(UsageEvents.Event event) {
+ if (events == null) {
+ events = new EventList();
+ }
+ // Cache common use strings
+ event.mPackage = getCachedStringRef(event.mPackage);
+ if (event.mClass != null) {
+ event.mClass = getCachedStringRef(event.mClass);
+ }
+ if (event.mEventType == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId);
+ }
+ events.insert(event);
+ }
+
void updateChooserCounts(String packageName, String category, String action) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
if (usageStats.mChooserCounts == null) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 5ab5dc2..c616685 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,6 +17,7 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
+import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.os.Build;
@@ -25,6 +26,10 @@
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.io.IoUtils;
+
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
@@ -32,18 +37,49 @@
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
+import java.io.InputStream;
import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
/**
- * Provides an interface to query for UsageStat data from an XML database.
+ * Provides an interface to query for UsageStat data from a Protocol Buffer database.
+ *
+ * Prior to version 4, UsageStatsDatabase used XML to store Usage Stats data to disk.
+ * When the UsageStatsDatabase version is upgraded, the files on disk are migrated to the new
+ * version on init. The steps of migration are as follows:
+ * 1) Check if version upgrade breadcrumb exists on disk, if so skip to step 4.
+ * 2) Copy current files to versioned backup files.
+ * 3) Write a temporary breadcrumb file with some info about the backed up files.
+ * 4) Deserialize a versioned backup file using the info written to the breadcrumb for the
+ * correct deserialization methodology.
+ * 5) Reserialize the data read from the file with the new version format and replace the old files
+ * 6) Repeat Step 3 and 4 for each versioned backup file matching the breadcrumb file.
+ * 7) Update the version file with the new version and build fingerprint.
+ * 8) Delete the versioned backup files (unless flagged to be kept).
+ * 9) Delete the breadcrumb file.
+ *
+ * Performing the upgrade steps in this order, protects against unexpected shutdowns mid upgrade
+ *
+ * A versioned backup file is simply a copy of a Usage Stats file with some extra info embedded in
+ * the file name. The structure of the versioned backup filename is as followed:
+ * (original file name).(backup timestamp).(original file version).vak
+ *
+ * During the version upgrade process, the new upgraded file will have it's name set to the original
+ * file name. The backup timestamp helps distinguish between versioned backups if multiple upgrades
+ * and downgrades have taken place. The original file version denotes how to parse the file.
*/
public class UsageStatsDatabase {
- private static final int CURRENT_VERSION = 3;
+ private static final int DEFAULT_CURRENT_VERSION = 4;
// Current version of the backup schema
static final int BACKUP_VERSION = 1;
@@ -52,10 +88,16 @@
// same as UsageStatsBackupHelper.KEY_USAGE_STATS
static final String KEY_USAGE_STATS = "usage_stats";
+ // Persist versioned backup files.
+ // Should be false, except when testing new versions
+ // STOPSHIP: b/111422946 this should be false on launch
+ static final boolean KEEP_VAK_FILES = true;
private static final String TAG = "UsageStatsDatabase";
- private static final boolean DEBUG = UsageStatsService.DEBUG;
+ // STOPSHIP: b/111422946 this should be boolean DEBUG = UsageStatsService.DEBUG; on launch
+ private static final boolean DEBUG = true;
private static final String BAK_SUFFIX = ".bak";
+ private static final String VERSIONED_BAK_SUFFIX = ".vak";
private static final String CHECKED_IN_SUFFIX = UsageStatsXml.CHECKED_IN_SUFFIX;
private static final String RETENTION_LEN_KEY = "ro.usagestats.chooser.retention";
private static final int SELECTION_LOG_RETENTION_LEN =
@@ -66,21 +108,40 @@
private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
private final UnixCalendar mCal;
private final File mVersionFile;
+ // If this file exists on disk, UsageStatsDatabase is in the middle of migrating files to a new
+ // version. If this file exists on boot, the upgrade was interrupted and needs to be picked up
+ // where it left off.
+ private final File mUpdateBreadcrumb;
+ // Current version of the database files schema
+ private final int mCurrentVersion;
private boolean mFirstUpdate;
private boolean mNewUpdate;
- public UsageStatsDatabase(File dir) {
- mIntervalDirs = new File[] {
+ /**
+ * UsageStatsDatabase constructor that allows setting the version number.
+ * This should only be used for testing.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public UsageStatsDatabase(File dir, int version) {
+ mIntervalDirs = new File[]{
new File(dir, "daily"),
new File(dir, "weekly"),
new File(dir, "monthly"),
new File(dir, "yearly"),
};
+ mCurrentVersion = version;
mVersionFile = new File(dir, "version");
+ mUpdateBreadcrumb = new File(dir, "breadcrumb");
mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
mCal = new UnixCalendar(0);
}
+ public UsageStatsDatabase(File dir) {
+ this(dir, DEFAULT_CURRENT_VERSION);
+ }
+
/**
* Initialize any directories required and index what stats are available.
*/
@@ -154,7 +215,7 @@
try {
IntervalStats stats = new IntervalStats();
for (int i = start; i < fileCount - 1; i++) {
- UsageStatsXml.read(files.valueAt(i), stats);
+ readLocked(files.valueAt(i), stats);
if (!checkinAction.checkin(stats)) {
return false;
}
@@ -190,7 +251,7 @@
final FilenameFilter backupFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
- return !name.endsWith(BAK_SUFFIX);
+ return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX);
}
};
@@ -210,7 +271,7 @@
for (File f : files) {
final AtomicFile af = new AtomicFile(f);
try {
- mSortedStatFiles[i].put(UsageStatsXml.parseBeginTime(af), af);
+ mSortedStatFiles[i].put(parseBeginTime(af), af);
} catch (IOException e) {
Slog.e(TAG, "failed to index file: " + f, e);
}
@@ -252,14 +313,32 @@
version = 0;
}
- if (version != CURRENT_VERSION) {
- Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION);
- doUpgradeLocked(version);
+ if (version != mCurrentVersion) {
+ Slog.i(TAG, "Upgrading from version " + version + " to " + mCurrentVersion);
+ if (!mUpdateBreadcrumb.exists()) {
+ doUpgradeLocked(version);
+ } else {
+ Slog.i(TAG, "Version upgrade breadcrumb found on disk! Continuing version upgrade");
+ }
+
+ if (mUpdateBreadcrumb.exists()) {
+ int previousVersion;
+ long token;
+ try (BufferedReader reader = new BufferedReader(
+ new FileReader(mUpdateBreadcrumb))) {
+ token = Long.parseLong(reader.readLine());
+ previousVersion = Integer.parseInt(reader.readLine());
+ } catch (NumberFormatException | IOException e) {
+ Slog.e(TAG, "Failed read version upgrade breadcrumb");
+ throw new RuntimeException(e);
+ }
+ continueUpgradeLocked(previousVersion, token);
+ }
}
- if (version != CURRENT_VERSION || mNewUpdate) {
+ if (version != mCurrentVersion || mNewUpdate) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) {
- writer.write(Integer.toString(CURRENT_VERSION));
+ writer.write(Integer.toString(mCurrentVersion));
writer.write("\n");
writer.write(currentFingerprint);
writer.write("\n");
@@ -269,6 +348,14 @@
throw new RuntimeException(e);
}
}
+
+ if (mUpdateBreadcrumb.exists()) {
+ // Files should be up to date with current version. Clear the version update breadcrumb
+ if (!KEEP_VAK_FILES) {
+ removeVersionedBackupFiles();
+ }
+ mUpdateBreadcrumb.delete();
+ }
}
private String getBuildFingerprint() {
@@ -290,6 +377,119 @@
}
}
}
+ } else {
+ // Turn all current usage stats files into versioned backup files
+ final long token = System.currentTimeMillis();
+ final FilenameFilter backupFileFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX);
+ }
+ };
+
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ File[] files = mIntervalDirs[i].listFiles(backupFileFilter);
+ if (files != null) {
+ for (int j = 0; j < files.length; j++) {
+ final File backupFile = new File(
+ files[j].toString() + "." + Long.toString(token) + "."
+ + Integer.toString(thisVersion) + VERSIONED_BAK_SUFFIX);
+ if (DEBUG) {
+ Slog.d(TAG, "Creating versioned (" + Integer.toString(thisVersion)
+ + ") backup of " + files[j].toString()
+ + " stat files for interval "
+ + i + " to " + backupFile.toString());
+ }
+
+ try {
+ // Backup file should not already exist, but make sure it doesn't
+ Files.deleteIfExists(backupFile.toPath());
+ Files.move(files[j].toPath(), backupFile.toPath(),
+ StandardCopyOption.ATOMIC_MOVE);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to back up file : " + files[j].toString());
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ // Leave a breadcrumb behind noting that all the usage stats have been copied to a
+ // versioned backup.
+ BufferedWriter writer = null;
+ try {
+ writer = new BufferedWriter(new FileWriter(mUpdateBreadcrumb));
+ writer.write(Long.toString(token));
+ writer.write("\n");
+ writer.write(Integer.toString(thisVersion));
+ writer.write("\n");
+ writer.flush();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write new version upgrade breadcrumb");
+ throw new RuntimeException(e);
+ } finally {
+ IoUtils.closeQuietly(writer);
+ }
+ }
+ }
+
+ private void continueUpgradeLocked(int version, long token) {
+ // Read all the backed ups for the specified version and rewrite them with the current
+ // version's file format.
+ final FilenameFilter versionedBackupFileFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith("." + Long.toString(token) + "." + Integer.toString(version)
+ + VERSIONED_BAK_SUFFIX);
+ }
+ };
+
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter);
+ if (files != null) {
+ for (int j = 0; j < files.length; j++) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Upgrading " + files[j].toString() + " to version ("
+ + Integer.toString(
+ mCurrentVersion) + ") for interval " + i);
+ }
+ try {
+ IntervalStats stats = new IntervalStats();
+ readLocked(new AtomicFile(files[j]), stats, version);
+ writeLocked(new AtomicFile(new File(mIntervalDirs[i],
+ Long.toString(stats.beginTime))), stats, mCurrentVersion);
+ } catch (IOException e) {
+ Slog.e(TAG,
+ "Failed to upgrade versioned backup file : " + files[j].toString());
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+ private void removeVersionedBackupFiles() {
+ final FilenameFilter versionedBackupFileFilter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ return name.endsWith(VERSIONED_BAK_SUFFIX);
+ }
+ };
+
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter);
+ if (files != null) {
+ for (int j = 0; j < files.length; j++) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Removing " + files[j].toString() + " for interval " + i);
+ }
+ if (!files[j].delete()) {
+ Slog.e(TAG, "Failed to delete file : " + files[j].toString());
+ }
+ }
+ }
}
}
@@ -357,7 +557,7 @@
try {
final AtomicFile f = mSortedStatFiles[intervalType].valueAt(fileCount - 1);
IntervalStats stats = new IntervalStats();
- UsageStatsXml.read(f, stats);
+ readLocked(f, stats);
return stats;
} catch (IOException e) {
Slog.e(TAG, "Failed to read usage stats file", e);
@@ -379,8 +579,8 @@
* which means you should make a copy of the data before adding it to the
* <code>accumulatedResult</code> list.
*
- * @param stats The {@link IntervalStats} object selected.
- * @param mutable Whether or not the data inside the stats object is mutable.
+ * @param stats The {@link IntervalStats} object selected.
+ * @param mutable Whether or not the data inside the stats object is mutable.
* @param accumulatedResult The list to which to add extracted data.
*/
void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
@@ -443,7 +643,7 @@
}
try {
- UsageStatsXml.read(f, stats);
+ readLocked(f, stats);
if (beginTime < stats.endTime) {
combiner.combine(stats, false, results);
}
@@ -523,14 +723,9 @@
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
- String path = f.getPath();
- if (path.endsWith(BAK_SUFFIX)) {
- f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
- }
-
long beginTime;
try {
- beginTime = UsageStatsXml.parseBeginTime(f);
+ beginTime = parseBeginTime(f);
} catch (IOException e) {
beginTime = 0;
}
@@ -542,18 +737,13 @@
}
}
- private static void pruneChooserCountsOlderThan(File dir, long expiryTime) {
+ private void pruneChooserCountsOlderThan(File dir, long expiryTime) {
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
- String path = f.getPath();
- if (path.endsWith(BAK_SUFFIX)) {
- f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
- }
-
long beginTime;
try {
- beginTime = UsageStatsXml.parseBeginTime(f);
+ beginTime = parseBeginTime(f);
} catch (IOException e) {
beginTime = 0;
}
@@ -562,7 +752,7 @@
try {
final AtomicFile af = new AtomicFile(f);
final IntervalStats stats = new IntervalStats();
- UsageStatsXml.read(af, stats);
+ readLocked(af, stats);
final int pkgCount = stats.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
UsageStats pkgStats = stats.packageStats.valueAt(i);
@@ -570,7 +760,7 @@
pkgStats.mChooserCounts.clear();
}
}
- UsageStatsXml.write(af, stats);
+ writeLocked(af, stats);
} catch (IOException e) {
Slog.e(TAG, "Failed to delete chooser counts from usage stats file", e);
}
@@ -579,6 +769,222 @@
}
}
+
+ private static long parseBeginTime(AtomicFile file) throws IOException {
+ return parseBeginTime(file.getBaseFile());
+ }
+
+ private static long parseBeginTime(File file) throws IOException {
+ String name = file.getName();
+
+ // Parse out the digits from the the front of the file name
+ for (int i = 0; i < name.length(); i++) {
+ final char c = name.charAt(i);
+ if (c < '0' || c > '9') {
+ // found first char that is not a digit.
+ name = name.substring(0, i);
+ break;
+ }
+ }
+
+ try {
+ return Long.parseLong(name);
+ } catch (NumberFormatException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException {
+ writeLocked(file, stats, mCurrentVersion);
+ }
+
+ private static void writeLocked(AtomicFile file, IntervalStats stats, int version)
+ throws IOException {
+ FileOutputStream fos = file.startWrite();
+ try {
+ writeLocked(fos, stats, version);
+ file.finishWrite(fos);
+ fos = null;
+ } finally {
+ // When fos is null (successful write), this will no-op
+ file.failWrite(fos);
+ }
+ }
+
+ private void writeLocked(OutputStream out, IntervalStats stats) throws IOException {
+ writeLocked(out, stats, mCurrentVersion);
+ }
+
+ private static void writeLocked(OutputStream out, IntervalStats stats, int version)
+ throws IOException {
+ switch (version) {
+ case 1:
+ case 2:
+ case 3:
+ UsageStatsXml.write(out, stats);
+ break;
+ case 4:
+ UsageStatsProto.write(out, stats);
+ break;
+ default:
+ throw new RuntimeException(
+ "Unhandled UsageStatsDatabase version: " + Integer.toString(version)
+ + " on write.");
+ }
+ }
+
+ private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException {
+ readLocked(file, statsOut, mCurrentVersion);
+ }
+
+ private static void readLocked(AtomicFile file, IntervalStats statsOut, int version)
+ throws IOException {
+ try {
+ FileInputStream in = file.openRead();
+ try {
+ statsOut.beginTime = parseBeginTime(file);
+ readLocked(in, statsOut, version);
+ statsOut.lastTimeSaved = file.getLastModifiedTime();
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // Empty
+ }
+ }
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "UsageStatsDatabase", e);
+ throw e;
+ }
+ // STOPSHIP: b/111422946, b/115429334
+ // Everything below this comment is sanity check against the new database version.
+ // After the new version has soaked for some time the following should removed.
+ // The goal of this check is to make sure the the ProtoInputStream is properly reading from
+ // the UsageStats files.
+ final StringBuilder sb = new StringBuilder();
+ final int failureLogLimit = 10;
+ int failures = 0;
+
+ final int packagesSize = statsOut.packageStats.size();
+ for (int i = 0; i < packagesSize; i++) {
+ final UsageStats stat = statsOut.packageStats.valueAt(i);
+ if (stat == null) {
+ // ArrayMap may contain null values, skip them
+ continue;
+ }
+ if (stat.mPackageName.isEmpty()) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected empty usage stats package name loaded");
+ }
+ }
+ if (stat.mBeginTimeStamp > statsOut.endTime) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnreasonable usage stats stat begin timestamp ");
+ sb.append(stat.mBeginTimeStamp);
+ sb.append(" loaded (beginTime : ");
+ sb.append(statsOut.beginTime);
+ sb.append(", endTime : ");
+ sb.append(statsOut.endTime);
+ sb.append(")");
+ }
+ }
+ if (stat.mEndTimeStamp > statsOut.endTime) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnreasonable usage stats stat end timestamp ");
+ sb.append(stat.mEndTimeStamp);
+ sb.append(" loaded (beginTime : ");
+ sb.append(statsOut.beginTime);
+ sb.append(", endTime : ");
+ sb.append(statsOut.endTime);
+ sb.append(")");
+ }
+ }
+ if (stat.mLastTimeUsed > statsOut.endTime) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnreasonable usage stats stat last used timestamp ");
+ sb.append(stat.mLastTimeUsed);
+ sb.append(" loaded (beginTime : ");
+ sb.append(statsOut.beginTime);
+ sb.append(", endTime : ");
+ sb.append(statsOut.endTime);
+ sb.append(")");
+ }
+ }
+ }
+
+ if (statsOut.events != null) {
+ final int eventSize = statsOut.events.size();
+ for (int i = 0; i < eventSize; i++) {
+ final UsageEvents.Event event = statsOut.events.get(i);
+ if (event.mPackage.isEmpty()) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected empty empty package name loaded");
+ }
+ }
+ if (event.mTimeStamp < statsOut.beginTime || event.mTimeStamp > statsOut.endTime) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected event timestamp ");
+ sb.append(event.mTimeStamp);
+ sb.append(" loaded (beginTime : ");
+ sb.append(statsOut.beginTime);
+ sb.append(", endTime : ");
+ sb.append(statsOut.endTime);
+ sb.append(")");
+ }
+ }
+ if (event.mEventType < 0 || event.mEventType > UsageEvents.Event.MAX_EVENT_TYPE) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected event type ");
+ sb.append(event.mEventType);
+ sb.append(" loaded");
+ }
+ }
+ if ((event.mFlags & ~UsageEvents.Event.VALID_FLAG_BITS) != 0) {
+ if (failures++ < failureLogLimit) {
+ sb.append("\nUnexpected event flag bit 0b");
+ sb.append(Integer.toBinaryString(event.mFlags));
+ sb.append(" loaded");
+ }
+ }
+ }
+ }
+
+ if (failures != 0) {
+ if (failures > failureLogLimit) {
+ sb.append("\nFailure log limited (");
+ sb.append(failures);
+ sb.append(" total failures found!)");
+ }
+ sb.append("\nError found in:\n");
+ sb.append(file.getBaseFile().getAbsolutePath());
+ sb.append("\nPlease go to b/115429334 to help root cause this issue");
+ Slog.wtf(TAG,sb.toString());
+ }
+ }
+
+ private void readLocked(InputStream in, IntervalStats statsOut) throws IOException {
+ readLocked(in, statsOut, mCurrentVersion);
+ }
+
+ private static void readLocked(InputStream in, IntervalStats statsOut, int version)
+ throws IOException {
+ switch (version) {
+ case 1:
+ case 2:
+ case 3:
+ UsageStatsXml.read(in, statsOut);
+ break;
+ case 4:
+ UsageStatsProto.read(in, statsOut);
+ break;
+ default:
+ throw new RuntimeException(
+ "Unhandled UsageStatsDatabase version: " + Integer.toString(version)
+ + " on read.");
+ }
+
+ }
+
/**
* Update the stats in the database. They may not be written to disk immediately.
*/
@@ -596,7 +1002,7 @@
mSortedStatFiles[intervalType].put(stats.beginTime, f);
}
- UsageStatsXml.write(f, stats);
+ writeLocked(f, stats);
stats.lastTimeSaved = f.getLastModifiedTime();
}
}
@@ -730,7 +1136,7 @@
throws IOException {
IntervalStats stats = new IntervalStats();
try {
- UsageStatsXml.read(statsFile, stats);
+ readLocked(statsFile, stats);
} catch (IOException e) {
Slog.e(TAG, "Failed to read usage stats file", e);
out.writeInt(0);
@@ -756,12 +1162,12 @@
if (stats.events != null) stats.events.clear();
}
- private static byte[] serializeIntervalStats(IntervalStats stats) {
+ private byte[] serializeIntervalStats(IntervalStats stats) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
try {
out.writeLong(stats.beginTime);
- UsageStatsXml.write(out, stats);
+ writeLocked(out, stats);
} catch (IOException ioe) {
Slog.d(TAG, "Serializing IntervalStats Failed", ioe);
baos.reset();
@@ -769,13 +1175,13 @@
return baos.toByteArray();
}
- private static IntervalStats deserializeIntervalStats(byte[] data) {
+ private IntervalStats deserializeIntervalStats(byte[] data) {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
DataInputStream in = new DataInputStream(bais);
IntervalStats stats = new IntervalStats();
try {
stats.beginTime = in.readLong();
- UsageStatsXml.read(in, stats);
+ readLocked(in, stats);
} catch (IOException ioe) {
Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
stats = null;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
new file mode 100644
index 0000000..30d303f
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usage;
+
+import android.app.usage.ConfigurationStats;
+import android.app.usage.EventList;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.content.res.Configuration;
+import android.util.ArrayMap;
+
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * UsageStats reader/writer for Protocol Buffer format
+ */
+final class UsageStatsProto {
+ private static String TAG = "UsageStatsProto";
+
+ // Static-only utility class.
+ private UsageStatsProto() {}
+
+ private static List<String> readStringPool(ProtoInputStream proto) throws IOException {
+
+ final long token = proto.start(IntervalStatsProto.STRINGPOOL);
+ List<String> stringPool;
+ if (proto.isNextField(IntervalStatsProto.StringPool.SIZE)) {
+ stringPool = new ArrayList(proto.readInt(IntervalStatsProto.StringPool.SIZE));
+ } else {
+ stringPool = new ArrayList();
+ }
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) IntervalStatsProto.StringPool.STRINGS:
+ stringPool.add(proto.readString(IntervalStatsProto.StringPool.STRINGS));
+ break;
+ }
+ }
+ proto.end(token);
+ return stringPool;
+ }
+
+ private static void loadUsageStats(ProtoInputStream proto, long fieldId,
+ IntervalStats statsOut, List<String> stringPool)
+ throws IOException {
+
+ final long token = proto.start(fieldId);
+ UsageStats stats;
+ if (proto.isNextField(IntervalStatsProto.UsageStats.PACKAGE_INDEX)) {
+ // Fast path reading the package name index. Most cases this should work since it is
+ // written first
+ stats = statsOut.getOrCreateUsageStats(
+ stringPool.get(proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1));
+ } else if (proto.isNextField(IntervalStatsProto.UsageStats.PACKAGE)) {
+ // No package index, try package name instead
+ stats = statsOut.getOrCreateUsageStats(
+ proto.readString(IntervalStatsProto.UsageStats.PACKAGE));
+ } else {
+ // Temporarily store collected data to a UsageStats object. This is not efficient.
+ stats = new UsageStats();
+ }
+
+ while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (proto.getFieldNumber()) {
+ case (int) IntervalStatsProto.UsageStats.PACKAGE:
+ // Fast track failed from some reason, add UsageStats object to statsOut now
+ UsageStats tempPackage = statsOut.getOrCreateUsageStats(
+ proto.readString(IntervalStatsProto.UsageStats.PACKAGE));
+ tempPackage.mLastTimeUsed = stats.mLastTimeUsed;
+ tempPackage.mTotalTimeInForeground = stats.mTotalTimeInForeground;
+ tempPackage.mLastEvent = stats.mLastEvent;
+ tempPackage.mAppLaunchCount = stats.mAppLaunchCount;
+ stats = tempPackage;
+ break;
+ case (int) IntervalStatsProto.UsageStats.PACKAGE_INDEX:
+ // Fast track failed from some reason, add UsageStats object to statsOut now
+ UsageStats tempPackageIndex = statsOut.getOrCreateUsageStats(stringPool.get(
+ proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1));
+ tempPackageIndex.mLastTimeUsed = stats.mLastTimeUsed;
+ tempPackageIndex.mTotalTimeInForeground = stats.mTotalTimeInForeground;
+ tempPackageIndex.mLastEvent = stats.mLastEvent;
+ tempPackageIndex.mAppLaunchCount = stats.mAppLaunchCount;
+ stats = tempPackageIndex;
+ break;
+ case (int) IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS:
+ stats.mLastTimeUsed = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS:
+ stats.mTotalTimeInForeground = proto.readLong(
+ IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsProto.UsageStats.LAST_EVENT:
+ stats.mLastEvent = proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT);
+ break;
+ case (int) IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT:
+ stats.mAppLaunchCount = proto.readInt(
+ IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT);
+ break;
+ case (int) IntervalStatsProto.UsageStats.CHOOSER_ACTIONS:
+ final long chooserToken = proto.start(
+ IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
+ loadChooserCounts(proto, stats);
+ proto.end(chooserToken);
+ break;
+ }
+ }
+ if (stats.mLastTimeUsed == 0) {
+ // mLastTimeUsed was not assigned, assume default value of 0 plus beginTime;
+ stats.mLastTimeUsed = statsOut.beginTime;
+ }
+ proto.end(token);
+ }
+
+ private static void loadCountAndTime(ProtoInputStream proto, long fieldId,
+ IntervalStats.EventTracker tracker) throws IOException {
+ final long token = proto.start(fieldId);
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.CountAndTime.COUNT:
+ tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT);
+ break;
+ case (int) IntervalStatsProto.CountAndTime.TIME_MS:
+ tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ proto.end(token);
+ return;
+ }
+ }
+ }
+
+ private static void loadChooserCounts(ProtoInputStream proto, UsageStats usageStats)
+ throws IOException {
+ if (usageStats.mChooserCounts == null) {
+ usageStats.mChooserCounts = new ArrayMap<>();
+ }
+ String action = null;
+ ArrayMap<String, Integer> counts;
+ if (proto.isNextField(IntervalStatsProto.UsageStats.ChooserAction.NAME)) {
+ // Fast path reading the action name. Most cases this should work since it is written
+ // first
+ action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME);
+ counts = usageStats.mChooserCounts.get(action);
+ if (counts == null) {
+ counts = new ArrayMap<>();
+ usageStats.mChooserCounts.put(action, counts);
+ }
+ } else {
+ // Temporarily store collected data to an ArrayMap. This is not efficient.
+ counts = new ArrayMap<>();
+ }
+
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.UsageStats.ChooserAction.NAME:
+ // Fast path failed from some reason, add the ArrayMap object to usageStats now
+ action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME);
+ usageStats.mChooserCounts.put(action, counts);
+ break;
+ case (int) IntervalStatsProto.UsageStats.ChooserAction.COUNTS:
+ final long token = proto.start(
+ IntervalStatsProto.UsageStats.ChooserAction.COUNTS);
+ loadCountsForAction(proto, counts);
+ proto.end(token);
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (action == null) {
+ // default string
+ usageStats.mChooserCounts.put("", counts);
+ }
+ return;
+ }
+ }
+ }
+
+ private static void loadCountsForAction(ProtoInputStream proto,
+ ArrayMap<String, Integer> counts) throws IOException {
+ String category = null;
+ int count = 0;
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME:
+ category = proto.readString(
+ IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME);
+ break;
+ case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT:
+ count = proto.readInt(
+ IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (category == null) {
+ counts.put("", count);
+ } else {
+ counts.put(category, count);
+ }
+ return;
+ }
+ }
+ }
+
+ private static void loadConfigStats(ProtoInputStream proto, long fieldId,
+ IntervalStats statsOut) throws IOException {
+ final long token = proto.start(fieldId);
+ boolean configActive = false;
+ final Configuration config = new Configuration();
+ ConfigurationStats configStats;
+ if (proto.isNextField(IntervalStatsProto.Configuration.CONFIG)) {
+ // Fast path reading the configuration. Most cases this should work since it is
+ // written first
+ config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG);
+ configStats = statsOut.getOrCreateConfigurationStats(config);
+ } else {
+ // Temporarily store collected data to a ConfigurationStats object. This is not
+ // efficient.
+ configStats = new ConfigurationStats();
+ }
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.Configuration.CONFIG:
+ // Fast path failed from some reason, add ConfigStats object to statsOut now
+ config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG);
+ final ConfigurationStats temp = statsOut.getOrCreateConfigurationStats(config);
+ temp.mLastTimeActive = configStats.mLastTimeActive;
+ temp.mTotalTimeActive = configStats.mTotalTimeActive;
+ temp.mActivationCount = configStats.mActivationCount;
+ configStats = temp;
+ break;
+ case (int) IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS:
+ configStats.mLastTimeActive = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS:
+ configStats.mTotalTimeActive = proto.readLong(
+ IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS);
+ break;
+ case (int) IntervalStatsProto.Configuration.COUNT:
+ configStats.mActivationCount = proto.readInt(
+ IntervalStatsProto.Configuration.COUNT);
+ break;
+ case (int) IntervalStatsProto.Configuration.ACTIVE:
+ configActive = proto.readBoolean(IntervalStatsProto.Configuration.ACTIVE);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (configStats.mLastTimeActive == 0) {
+ //mLastTimeActive was not assigned, assume default value of 0 plus beginTime
+ configStats.mLastTimeActive = statsOut.beginTime;
+ }
+ if (configActive) {
+ statsOut.activeConfiguration = configStats.mConfiguration;
+ }
+ proto.end(token);
+ return;
+ }
+ }
+ }
+
+ private static void loadEvent(ProtoInputStream proto, long fieldId, IntervalStats statsOut,
+ List<String> stringPool) throws IOException {
+ final long token = proto.start(fieldId);
+ UsageEvents.Event event = statsOut.buildEvent(proto, stringPool);
+ proto.end(token);
+ if (event.mPackage == null) {
+ throw new ProtocolException("no package field present");
+ }
+
+ if (statsOut.events == null) {
+ statsOut.events = new EventList();
+ }
+ statsOut.events.insert(event);
+ }
+
+ private static void writeStringPool(ProtoOutputStream proto, final IntervalStats stats)
+ throws IOException {
+ final long token = proto.start(IntervalStatsProto.STRINGPOOL);
+ final int size = stats.mStringCache.size();
+ proto.write(IntervalStatsProto.StringPool.SIZE, size);
+ for (int i = 0; i < size; i++) {
+ proto.write(IntervalStatsProto.StringPool.STRINGS, stats.mStringCache.valueAt(i));
+ }
+ proto.end(token);
+ }
+
+ private static void writeUsageStats(ProtoOutputStream proto, long fieldId,
+ final IntervalStats stats, final UsageStats usageStats) throws IOException {
+ final long token = proto.start(fieldId);
+ // Write the package name first, so loadUsageStats can avoid creating an extra object
+ final int packageIndex = stats.mStringCache.indexOf(usageStats.mPackageName);
+ if (packageIndex >= 0) {
+ proto.write(IntervalStatsProto.UsageStats.PACKAGE_INDEX, packageIndex + 1);
+ } else {
+ // Package not in Stringpool for some reason, write full string instead
+ Slog.w(TAG, "UsageStats package name (" + usageStats.mPackageName
+ + ") not found in IntervalStats string cache");
+ proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName);
+ }
+ proto.write(IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS,
+ usageStats.mLastTimeUsed - stats.beginTime);
+ proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS,
+ usageStats.mTotalTimeInForeground);
+ proto.write(IntervalStatsProto.UsageStats.LAST_EVENT, usageStats.mLastEvent);
+ proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
+ writeChooserCounts(proto, usageStats);
+ proto.end(token);
+ }
+
+ private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count,
+ long time) throws IOException {
+ final long token = proto.start(fieldId);
+ proto.write(IntervalStatsProto.CountAndTime.COUNT, count);
+ proto.write(IntervalStatsProto.CountAndTime.TIME_MS, time);
+ proto.end(token);
+ }
+
+
+ private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats usageStats)
+ throws IOException {
+ if (usageStats == null || usageStats.mChooserCounts == null
+ || usageStats.mChooserCounts.keySet().isEmpty()) {
+ return;
+ }
+ final int chooserCountSize = usageStats.mChooserCounts.size();
+ for (int i = 0; i < chooserCountSize; i++) {
+ final String action = usageStats.mChooserCounts.keyAt(i);
+ final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
+ if (action == null || counts == null || counts.isEmpty()) {
+ continue;
+ }
+ final long token = proto.start(IntervalStatsProto.UsageStats.CHOOSER_ACTIONS);
+ proto.write(IntervalStatsProto.UsageStats.ChooserAction.NAME, action);
+ writeCountsForAction(proto, counts);
+ proto.end(token);
+ }
+ }
+
+ private static void writeCountsForAction(ProtoOutputStream proto,
+ ArrayMap<String, Integer> counts) throws IOException {
+ final int countsSize = counts.size();
+ for (int i = 0; i < countsSize; i++) {
+ String key = counts.keyAt(i);
+ int count = counts.valueAt(i);
+ if (count > 0) {
+ final long token = proto.start(IntervalStatsProto.UsageStats.ChooserAction.COUNTS);
+ proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME, key);
+ proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT, count);
+ proto.end(token);
+ }
+ }
+ }
+
+ private static void writeConfigStats(ProtoOutputStream proto, long fieldId,
+ final IntervalStats stats, final ConfigurationStats configStats, boolean isActive)
+ throws IOException {
+ final long token = proto.start(fieldId);
+ configStats.mConfiguration.writeToProto(proto, IntervalStatsProto.Configuration.CONFIG);
+ proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
+ configStats.mLastTimeActive - stats.beginTime);
+ proto.write(IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS,
+ configStats.mTotalTimeActive);
+ proto.write(IntervalStatsProto.Configuration.COUNT, configStats.mActivationCount);
+ proto.write(IntervalStatsProto.Configuration.ACTIVE, isActive);
+ proto.end(token);
+
+ }
+
+ private static void writeEvent(ProtoOutputStream proto, long fieldId, final IntervalStats stats,
+ final UsageEvents.Event event) throws IOException {
+ final long token = proto.start(fieldId);
+ final int packageIndex = stats.mStringCache.indexOf(event.mPackage);
+ if (packageIndex >= 0) {
+ proto.write(IntervalStatsProto.Event.PACKAGE_INDEX, packageIndex + 1);
+ } else {
+ // Package not in Stringpool for some reason, write full string instead
+ Slog.w(TAG, "Usage event package name (" + event.mPackage
+ + ") not found in IntervalStats string cache");
+ proto.write(IntervalStatsProto.Event.PACKAGE, event.mPackage);
+ }
+ if (event.mClass != null) {
+ final int classIndex = stats.mStringCache.indexOf(event.mClass);
+ if (classIndex >= 0) {
+ proto.write(IntervalStatsProto.Event.CLASS_INDEX, classIndex + 1);
+ } else {
+ // Class not in Stringpool for some reason, write full string instead
+ Slog.w(TAG, "Usage event class name (" + event.mClass
+ + ") not found in IntervalStats string cache");
+ proto.write(IntervalStatsProto.Event.CLASS, event.mClass);
+ }
+ }
+ proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime);
+ proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags);
+ proto.write(IntervalStatsProto.Event.TYPE, event.mEventType);
+ switch (event.mEventType) {
+ case UsageEvents.Event.CONFIGURATION_CHANGE:
+ if (event.mConfiguration != null) {
+ event.mConfiguration.writeToProto(proto, IntervalStatsProto.Event.CONFIG);
+ }
+ break;
+ case UsageEvents.Event.SHORTCUT_INVOCATION:
+ if (event.mShortcutId != null) {
+ proto.write(IntervalStatsProto.Event.SHORTCUT_ID, event.mShortcutId);
+ }
+ break;
+ case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ if (event.mBucketAndReason != 0) {
+ proto.write(IntervalStatsProto.Event.STANDBY_BUCKET, event.mBucketAndReason);
+ }
+ break;
+ case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ if (event.mNotificationChannelId != null) {
+ final int channelIndex = stats.mStringCache.indexOf(
+ event.mNotificationChannelId);
+ if (channelIndex >= 0) {
+ proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX,
+ channelIndex + 1);
+ } else {
+ // Channel not in Stringpool for some reason, write full string instead
+ Slog.w(TAG, "Usage event notification channel name ("
+ + event.mNotificationChannelId
+ + ") not found in IntervalStats string cache");
+ proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL,
+ event.mNotificationChannelId);
+ }
+ }
+ break;
+ }
+ proto.end(token);
+ }
+
+ /**
+ * Reads from the {@link ProtoInputStream}.
+ *
+ * @param proto The proto from which to read events.
+ * @param statsOut The stats object to populate with the data from the XML file.
+ */
+ public static void read(InputStream in, IntervalStats statsOut) throws IOException {
+ final ProtoInputStream proto = new ProtoInputStream(in);
+ List<String> stringPool = null;
+
+ statsOut.packageStats.clear();
+ statsOut.configurations.clear();
+ statsOut.activeConfiguration = null;
+
+ if (statsOut.events != null) {
+ statsOut.events.clear();
+ }
+
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) IntervalStatsProto.END_TIME_MS:
+ statsOut.endTime = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.END_TIME_MS);
+ break;
+ case (int) IntervalStatsProto.INTERACTIVE:
+ loadCountAndTime(proto, IntervalStatsProto.INTERACTIVE,
+ statsOut.interactiveTracker);
+ break;
+ case (int) IntervalStatsProto.NON_INTERACTIVE:
+ loadCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
+ statsOut.nonInteractiveTracker);
+ break;
+ case (int) IntervalStatsProto.KEYGUARD_SHOWN:
+ loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
+ statsOut.keyguardShownTracker);
+ break;
+ case (int) IntervalStatsProto.KEYGUARD_HIDDEN:
+ loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
+ statsOut.keyguardHiddenTracker);
+ break;
+ case (int) IntervalStatsProto.STRINGPOOL:
+ stringPool = readStringPool(proto);
+ statsOut.mStringCache.addAll(stringPool);
+ break;
+ case (int) IntervalStatsProto.PACKAGES:
+ loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool);
+ break;
+ case (int) IntervalStatsProto.CONFIGURATIONS:
+ loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut);
+ break;
+ case (int) IntervalStatsProto.EVENT_LOG:
+ loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ if (statsOut.endTime == 0) {
+ // endTime not assigned, assume default value of 0 plus beginTime
+ statsOut.endTime = statsOut.beginTime;
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Writes the stats object to an ProtoBuf file.
+ *
+ * @param proto The serializer to which to write the packageStats data.
+ * @param stats The stats object to write to the XML file.
+ */
+ public static void write(OutputStream out, IntervalStats stats) throws IOException {
+ final ProtoOutputStream proto = new ProtoOutputStream(out);
+ proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
+ // String pool should be written before the rest of the usage stats
+ writeStringPool(proto, stats);
+
+ writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count,
+ stats.interactiveTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE,
+ stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN,
+ stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration);
+ writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN,
+ stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration);
+
+ final int statsCount = stats.packageStats.size();
+ for (int i = 0; i < statsCount; i++) {
+ writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats,
+ stats.packageStats.valueAt(i));
+ }
+ final int configCount = stats.configurations.size();
+ for (int i = 0; i < configCount; i++) {
+ boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
+ writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats,
+ stats.configurations.valueAt(i), active);
+ }
+ final int eventCount = stats.events != null ? stats.events.size() : 0;
+ for (int i = 0; i < eventCount; i++) {
+ writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i));
+ }
+
+ proto.flush();
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXml.java b/services/usage/java/com/android/server/usage/UsageStatsXml.java
index e7db741..f8d1113 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXml.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXml.java
@@ -19,6 +19,9 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -33,61 +36,7 @@
private static final String VERSION_ATTR = "version";
static final String CHECKED_IN_SUFFIX = "-c";
- public static long parseBeginTime(AtomicFile file) throws IOException {
- return parseBeginTime(file.getBaseFile());
- }
-
- public static long parseBeginTime(File file) throws IOException {
- String name = file.getName();
-
- // Eat as many occurrences of -c as possible. This is due to a bug where -c
- // would be appended more than once to a checked-in file, causing a crash
- // on boot when indexing files since Long.parseLong() will puke on anything but
- // a number.
- while (name.endsWith(CHECKED_IN_SUFFIX)) {
- name = name.substring(0, name.length() - CHECKED_IN_SUFFIX.length());
- }
-
- try {
- return Long.parseLong(name);
- } catch (NumberFormatException e) {
- throw new IOException(e);
- }
- }
-
- public static void read(AtomicFile file, IntervalStats statsOut) throws IOException {
- try {
- FileInputStream in = file.openRead();
- try {
- statsOut.beginTime = parseBeginTime(file);
- read(in, statsOut);
- statsOut.lastTimeSaved = file.getLastModifiedTime();
- } finally {
- try {
- in.close();
- } catch (IOException e) {
- // Empty
- }
- }
- } catch (FileNotFoundException e) {
- Slog.e(TAG, "UsageStats Xml", e);
- throw e;
- }
- }
-
- public static void write(AtomicFile file, IntervalStats stats) throws IOException {
- FileOutputStream fos = file.startWrite();
- try {
- write(fos, stats);
- file.finishWrite(fos);
- fos = null;
- } finally {
- // When fos is null (successful write), this will no-op
- file.failWrite(fos);
- }
- }
-
- static void read(InputStream in, IntervalStats statsOut) throws IOException {
+ public static void read(InputStream in, IntervalStats statsOut) throws IOException {
XmlPullParser parser = Xml.newPullParser();
try {
parser.setInput(in, "utf-8");
@@ -113,7 +62,7 @@
}
}
- static void write(OutputStream out, IntervalStats stats) throws IOException {
+ public static void write(OutputStream out, IntervalStats stats) throws IOException {
FastXmlSerializer xml = new FastXmlSerializer();
xml.setOutput(out, "utf-8");
xml.startDocument("utf-8", true);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index 6a1e97a..a68f9d3 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -196,11 +196,7 @@
event.mNotificationChannelId = (channelId != null) ? channelId.intern() : null;
break;
}
-
- if (statsOut.events == null) {
- statsOut.events = new EventList();
- }
- statsOut.events.insert(event);
+ statsOut.addEvent(event);
}
private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 9b194e9..1a8aba0 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -176,12 +176,8 @@
currentDailyStats.activeConfiguration, newFullConfig);
}
- // Add the event to the daily list.
- if (currentDailyStats.events == null) {
- currentDailyStats.events = new EventList();
- }
if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
- currentDailyStats.events.insert(event);
+ currentDailyStats.addEvent(event);
}
boolean incrementAppLaunch = false;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 995418e..57b652e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -271,6 +271,14 @@
KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool";
/**
+ * Do only allow auto selection in Advanced Network Settings when in home network.
+ * Manual selection is allowed when in roaming network.
+ * @hide
+ */
+ public static final String
+ KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network";
+
+ /**
* Control whether users receive a simplified network settings UI and improved network
* selection.
*/
@@ -2181,6 +2189,7 @@
sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
+ sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false);
sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 8e99518..5e4518f 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -32,7 +32,11 @@
/**
* Represents the neighboring cell information, including
* Received Signal Strength and Cell ID location.
+ *
+ * @deprecated This class should not be used by anyone targeting SDK level 29 (Q) or higher.
+ * Instead callers should use {@Link android.telephony.CellInfo}.
*/
+@Deprecated
public class NeighboringCellInfo implements Parcelable
{
/**
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 38ee79f..d1091f4 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2153,7 +2153,12 @@
/**
* Set preferred default data.
- * Set on which slot default data will be on.
+ * Set on which slot most cellular data will be on.
+ * It's also usually what we set up internet connection on.
+ *
+ * PreferredData overwrites user setting of default data subscription. And it's used
+ * by ANAS or carrier apps to switch primary and CBRS subscription dynamically in multi-SIM
+ * devices.
*
* @param slotId which slot is preferred to for cellular data.
* @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 824533d..ea9ac39 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1590,6 +1590,7 @@
*
* @return List of NeighboringCellInfo or null if info unavailable.
*
+ * @removed
* @deprecated Use {@link #getAllCellInfo} which returns a superset of the information
* from NeighboringCellInfo, including LTE cell information.
*/
diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
index 39ecb7e5a..122edba 100644
--- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java
@@ -69,17 +69,12 @@
private ConnectivityManager mCm;
private Context mContext;
private final static int SOCKET_TIMEOUT_MS = 100;
- private boolean mInetDiagUdpEnabled;
@Before
public void setUp() throws Exception {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mContext = instrumentation.getTargetContext();
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- int expectedUid = Process.myUid();
- UdpConnection udp = new UdpConnection("127.0.0.1", "127.0.0.2");
- int uid = mCm.getConnectionOwnerUid(udp.protocol, udp.local, udp.remote);
- mInetDiagUdpEnabled = (uid == expectedUid);
}
private class Connection {
@@ -188,11 +183,6 @@
tcp.close();
/**
- * TODO: STOPSHIP: Always test for UDP, do not allow opt-out.
- */
- if (!mInetDiagUdpEnabled) return;
-
- /**
* For UDP connections, either a complete match {protocol, local, remote} or a
* partial match {protocol, local} should return a valid UID.
*/
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fceaabd..1a05305 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4536,4 +4536,78 @@
mCellNetworkAgent.disconnect();
mCm.unregisterNetworkCallback(networkCallback);
}
+
+ @Test
+ public void testDataActivityTracking() throws RemoteException {
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ reset(mNetworkManagementService);
+ mCellNetworkAgent.connect(true);
+ networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_MOBILE));
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+
+ // Network switch
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.connect(true);
+ networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_WIFI));
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME));
+
+ // Disconnect wifi and switch back to cell
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.disconnect();
+ networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ assertNoCallbacks(networkCallback);
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_MOBILE));
+
+ // reconnect wifi
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ mWiFiNetworkAgent.connect(true);
+ networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+
+ // Disconnect cell
+ reset(mNetworkManagementService);
+ mCellNetworkAgent.disconnect();
+ networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ // LOST callback is triggered earlier than removing idle timer. Broadcast should also be
+ // sent as network being switched. Ensure rule removal for cell will not be triggered
+ // unexpectedly before network being removed.
+ waitForIdle();
+ verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
+ verify(mNetworkManagementService, times(1)).removeNetwork(
+ eq(mCellNetworkAgent.getNetwork().netId));
+
+ // Disconnect wifi
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.disconnect();
+ waitFor(cv);
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+
+ // Clean up
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 40d5544..a6ed9f2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -33,6 +33,7 @@
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -75,6 +76,8 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.dhcp.DhcpServer;
+import android.net.dhcp.DhcpServingParams;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
@@ -85,6 +88,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
+import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -146,6 +150,7 @@
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+ @Mock private DhcpServer mDhcpServer;
@Mock private INetd mNetd;
private final MockTetheringDependencies mTetheringDependencies =
@@ -240,6 +245,12 @@
public INetd getNetdService() {
return mNetd;
}
+
+ @Override
+ public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface,
+ DhcpServingParams params, SharedLog log) {
+ return mDhcpServer;
+ }
};
}
@@ -333,6 +344,7 @@
mServiceContext = new MockContext(mContext);
mContentResolver = new MockContentResolver(mServiceContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
mIntents = new Vector<>();
mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -343,12 +355,16 @@
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
mTetheringDependencies.reset();
- mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
- mLooper.getLooper(), mSystemProperties,
- mTetheringDependencies);
+ mTethering = makeTethering();
verify(mNMService).registerTetheringStatsProvider(any(), anyString());
}
+ private Tethering makeTethering() {
+ return new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
+ mLooper.getLooper(), mSystemProperties,
+ mTetheringDependencies);
+ }
+
@After
public void tearDown() {
mServiceContext.unregisterReceiver(mBroadcastReceiver);
@@ -597,6 +613,18 @@
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
+ verify(mDhcpServer, times(1)).start();
+ }
+
+ @Test
+ public void workingMobileUsbTethering_IPv4LegacyDhcp() {
+ Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
+ mTethering = makeTethering();
+ final NetworkState upstreamState = buildMobileIPv4UpstreamState();
+ runUsbTethering(upstreamState);
+ sendIPv6TetherUpdates(upstreamState);
+
+ verify(mDhcpServer, never()).start();
}
@Test
@@ -620,6 +648,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon, times(1)).start();
+ verify(mDhcpServer, times(1)).start();
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -633,6 +662,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mDhcpServer, times(1)).start();
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
TEST_XLAT_MOBILE_IFNAME);
@@ -649,6 +679,7 @@
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ verify(mDhcpServer, times(1)).start();
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
// Then 464xlat comes up
@@ -671,6 +702,8 @@
// Forwarding was not re-added for v6 (still times(1))
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+ // DHCP not restarted on downstream (still times(1))
+ verify(mDhcpServer, times(1)).start();
}
@Test
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index bb31230..5217784 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -225,13 +225,4 @@
final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
assertFalse(cfg.enableLegacyDhcpServer);
}
-
- @Test
- public void testNewDhcpServerDefault() {
- Settings.Global.putString(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, null);
-
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
- // TODO: change to false when new server is promoted to default
- assertTrue(cfg.enableLegacyDhcpServer);
- }
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7a91347..59ba8e7 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1698,9 +1698,7 @@
* @return the list of access points found in the most recent scan. An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} permission
- * in order to get valid results. If there is a remote exception (e.g., either a communication
- * problem with the system service or an exception within the framework) an empty list will be
- * returned.
+ * in order to get valid results.
*/
public List<ScanResult> getScanResults() {
try {