diff options
182 files changed, 4875 insertions, 1625 deletions
diff --git a/api/current.txt b/api/current.txt index 1b0971000f3a..7d6c9397e940 100755 --- a/api/current.txt +++ b/api/current.txt @@ -7,6 +7,7 @@ package android { 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"; @@ -773,6 +774,7 @@ package android { field public static final int isFeatureSplit = 16844123; // 0x101055b field public static final int isGame = 16843764; // 0x10103f4 field public static final int isIndicator = 16843079; // 0x1010147 + field public static final int isLightTheme = 16844175; // 0x101058f field public static final int isModifier = 16843334; // 0x1010246 field public static final int isRepeatable = 16843336; // 0x1010248 field public static final int isScrollContainer = 16843342; // 0x101024e @@ -32520,6 +32522,7 @@ package android.os { 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; @@ -32548,6 +32551,14 @@ package android.os { 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; @@ -42567,7 +42578,7 @@ package android.telephony { 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); @@ -43028,7 +43039,6 @@ package android.telephony { 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(); @@ -45039,11 +45049,20 @@ package android.text.style { ctor public TextAppearanceSpan(android.os.Parcel); method public int describeContents(); method public java.lang.String getFamily(); + method public java.lang.String getFontFeatureSettings(); + method public java.lang.String getFontVariationSettings(); method public android.content.res.ColorStateList getLinkTextColor(); + method public int getShadowColor(); + method public float getShadowDx(); + method public float getShadowDy(); + method public float getShadowRadius(); method public int getSpanTypeId(); method public android.content.res.ColorStateList getTextColor(); + method public int getTextFontWeight(); method public int getTextSize(); method public int getTextStyle(); + method public android.graphics.Typeface getTypeface(); + method public boolean isElegantTextHeight(); method public void updateDrawState(android.text.TextPaint); method public void updateMeasureState(android.text.TextPaint); method public void writeToParcel(android.os.Parcel, int); @@ -45686,6 +45705,7 @@ package android.util { 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(); @@ -45706,6 +45726,7 @@ package android.util { 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>); @@ -46277,6 +46298,7 @@ package android.util { 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); } @@ -46295,6 +46317,7 @@ package android.util { 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); } @@ -55244,6 +55267,7 @@ package dalvik.system { public final class DelegateLastClassLoader extends dalvik.system.PathClassLoader { ctor public DelegateLastClassLoader(java.lang.String, java.lang.ClassLoader); ctor public DelegateLastClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader); + ctor public DelegateLastClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader, boolean); } public class DexClassLoader extends dalvik.system.BaseDexClassLoader { diff --git a/api/removed.txt b/api/removed.txt index b6dabcd8614b..f7106d2207ec 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -545,6 +545,7 @@ package android.telephony { } 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 d3496ed9798b..e090ed14320c 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -99,6 +99,7 @@ statsd_common_shared_libraries := \ 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 2ef1169851ff..8da2d447a163 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -992,6 +992,60 @@ Status StatsService::sendAppBreadcrumbAtom(int32_t label, int32_t state) { 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 06189278abce..1f1d782af235 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 android { 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 @@ public: */ 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 1e9c354d26bf..30d8bfce5d2a 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -138,6 +138,9 @@ message Atom { 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. @@ -200,9 +203,10 @@ message AttributionNode { message KeyValuePair { optional int32 key = 1; oneof value { - int64 value_int = 2; - string value_str = 3; - float value_float = 4; + int32 value_int = 2; + int64 value_long = 3; + string value_str = 4; + float value_float = 5; } } @@ -769,6 +773,9 @@ message WakeupAlarmOccurred { // Name of the wakeup alarm. optional string tag = 2; + + // Name of source package (for historical reasons, since BatteryStats tracked it). + optional string package_name = 3; } /** @@ -1309,6 +1316,68 @@ message ChargeCyclesReported { } /** + * 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 66392f80f1fe..5fb196ff4884 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -163,10 +163,7 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { 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 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{}, {}, 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 f9f1b387279a..4bbcfd593366 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -92,7 +92,8 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, int32_t uid, - const std::map<int32_t, int64_t>& int_map, + 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, const std::map<int32_t, float>& float_map) { mLogdTimestampNs = wallClockTimestampNs; @@ -113,7 +114,7 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT pos[1]++; } - for (const auto&itr : string_map) { + for (const auto&itr : long_map) { pos[2] = 1; mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first))); pos[2] = 3; @@ -122,7 +123,7 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT pos[1]++; } - for (const auto&itr : float_map) { + for (const auto&itr : string_map) { pos[2] = 1; mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first))); pos[2] = 4; @@ -130,12 +131,142 @@ LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedT mValues.back().mField.decorateLastPos(2); pos[1]++; } + + for (const auto&itr : float_map) { + pos[2] = 1; + mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.first))); + pos[2] = 5; + mValues.push_back(FieldValue(Field(mTagId, pos, 2 /* depth */), Value(itr.second))); + mValues.back().mField.decorateLastPos(2); + pos[1]++; + } if (!mValues.empty()) { mValues.back().mField.decorateLastPos(1); mValues.at(mValues.size() - 2).mField.decorateLastPos(1); } } +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; @@ -213,9 +344,8 @@ bool LogEvent::write(float value) { return false; } - - -bool LogEvent::writeKeyValuePairs(const std::map<int32_t, int64_t>& int_map, +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, const std::map<int32_t, float>& float_map) { if (mContext) { @@ -233,6 +363,17 @@ bool LogEvent::writeKeyValuePairs(const std::map<int32_t, int64_t>& int_map, } } + for (const auto& itr : long_map) { + if (android_log_write_list_begin(mContext) < 0) { + return false; + } + write(itr.first); + write(itr.second); + if (android_log_write_list_end(mContext) < 0) { + return false; + } + } + for (const auto& itr : string_map) { if (android_log_write_list_begin(mContext) < 0) { return false; diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 9ef0bf469c14..c7e2a8c5ca21 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 { @@ -77,10 +80,32 @@ public: */ explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, int32_t uid, - const std::map<int32_t, int64_t>& int_map, + 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, 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(); /** @@ -122,7 +147,8 @@ public: bool write(float value); bool write(const std::vector<AttributionNodeInternal>& nodes); bool write(const AttributionNodeInternal& node); - bool writeKeyValuePairs(const std::map<int32_t, int64_t>& int_map, + bool 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, const std::map<int32_t, float>& float_map); diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index 9002f0773aaf..a5dac0836238 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 @@ int main(int /*argc*/, char** /*argv*/) { 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 9d9e5be9e165..dd3402dae2f8 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -781,7 +781,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, 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/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp index 6e3b04ce6b3b..d4907017dc6d 100644 --- a/cmds/statsd/tests/LogEvent_test.cpp +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -91,12 +91,16 @@ TEST(LogEventTest, TestLogParsing) { TEST(LogEventTest, TestKeyValuePairsAtomParsing) { LogEvent event1(83, 2000); - std::map<int32_t, int64_t> int_map; + std::map<int32_t, int32_t> int_map; + std::map<int32_t, int64_t> long_map; std::map<int32_t, std::string> string_map; std::map<int32_t, float> float_map; - int_map[11] = 123L; - int_map[22] = 345L; + int_map[11] = 123; + int_map[22] = 345; + + long_map[33] = 678L; + long_map[44] = 890L; string_map[1] = "test2"; string_map[2] = "test1"; @@ -104,12 +108,15 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { float_map[111] = 2.2f; float_map[222] = 1.1f; - EXPECT_TRUE(event1.writeKeyValuePairs(int_map, string_map, float_map)); + EXPECT_TRUE(event1.writeKeyValuePairs(int_map, + long_map, + string_map, + float_map)); event1.init(); EXPECT_EQ(83, event1.GetTagId()); const auto& items = event1.getValues(); - EXPECT_EQ((size_t)12, items.size()); + EXPECT_EQ((size_t)16, items.size()); const FieldValue& item0 = event1.getValues()[0]; EXPECT_EQ(0x2010101, item0.mField.getField()); @@ -118,8 +125,8 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { const FieldValue& item1 = event1.getValues()[1]; EXPECT_EQ(0x2010182, item1.mField.getField()); - EXPECT_EQ(Type::LONG, item1.mValue.getType()); - EXPECT_EQ(123L, item1.mValue.long_value); + EXPECT_EQ(Type::INT, item1.mValue.getType()); + EXPECT_EQ(123, item1.mValue.int_value); const FieldValue& item2 = event1.getValues()[2]; EXPECT_EQ(0x2010201, item2.mField.getField()); @@ -128,48 +135,68 @@ TEST(LogEventTest, TestKeyValuePairsAtomParsing) { const FieldValue& item3 = event1.getValues()[3]; EXPECT_EQ(0x2010282, item3.mField.getField()); - EXPECT_EQ(Type::LONG, item3.mValue.getType()); - EXPECT_EQ(345L, item3.mValue.long_value); + EXPECT_EQ(Type::INT, item3.mValue.getType()); + EXPECT_EQ(345, item3.mValue.int_value); const FieldValue& item4 = event1.getValues()[4]; EXPECT_EQ(0x2010301, item4.mField.getField()); EXPECT_EQ(Type::INT, item4.mValue.getType()); - EXPECT_EQ(1, item4.mValue.int_value); + EXPECT_EQ(33, item4.mValue.int_value); const FieldValue& item5 = event1.getValues()[5]; - EXPECT_EQ(0x2010383, item5.mField.getField()); - EXPECT_EQ(Type::STRING, item5.mValue.getType()); - EXPECT_EQ("test2", item5.mValue.str_value); + EXPECT_EQ(0x2010382, item5.mField.getField()); + EXPECT_EQ(Type::LONG, item5.mValue.getType()); + EXPECT_EQ(678L, item5.mValue.int_value); const FieldValue& item6 = event1.getValues()[6]; EXPECT_EQ(0x2010401, item6.mField.getField()); EXPECT_EQ(Type::INT, item6.mValue.getType()); - EXPECT_EQ(2, item6.mValue.int_value); + EXPECT_EQ(44, item6.mValue.int_value); const FieldValue& item7 = event1.getValues()[7]; - EXPECT_EQ(0x2010483, item7.mField.getField()); - EXPECT_EQ(Type::STRING, item7.mValue.getType()); - EXPECT_EQ("test1", item7.mValue.str_value); + EXPECT_EQ(0x2010482, item7.mField.getField()); + EXPECT_EQ(Type::LONG, item7.mValue.getType()); + EXPECT_EQ(890L, item7.mValue.int_value); const FieldValue& item8 = event1.getValues()[8]; EXPECT_EQ(0x2010501, item8.mField.getField()); EXPECT_EQ(Type::INT, item8.mValue.getType()); - EXPECT_EQ(111, item8.mValue.int_value); + EXPECT_EQ(1, item8.mValue.int_value); const FieldValue& item9 = event1.getValues()[9]; - EXPECT_EQ(0x2010584, item9.mField.getField()); - EXPECT_EQ(Type::FLOAT, item9.mValue.getType()); - EXPECT_EQ(2.2f, item9.mValue.float_value); + EXPECT_EQ(0x2010583, item9.mField.getField()); + EXPECT_EQ(Type::STRING, item9.mValue.getType()); + EXPECT_EQ("test2", item9.mValue.str_value); const FieldValue& item10 = event1.getValues()[10]; - EXPECT_EQ(0x2018601, item10.mField.getField()); + EXPECT_EQ(0x2010601, item10.mField.getField()); EXPECT_EQ(Type::INT, item10.mValue.getType()); - EXPECT_EQ(222, item10.mValue.int_value); + EXPECT_EQ(2, item10.mValue.int_value); const FieldValue& item11 = event1.getValues()[11]; - EXPECT_EQ(0x2018684, item11.mField.getField()); - EXPECT_EQ(Type::FLOAT, item11.mValue.getType()); - EXPECT_EQ(1.1f, item11.mValue.float_value); + EXPECT_EQ(0x2010683, item11.mField.getField()); + EXPECT_EQ(Type::STRING, item11.mValue.getType()); + EXPECT_EQ("test1", item11.mValue.str_value); + + const FieldValue& item12 = event1.getValues()[12]; + EXPECT_EQ(0x2010701, item12.mField.getField()); + EXPECT_EQ(Type::INT, item12.mValue.getType()); + EXPECT_EQ(111, item12.mValue.int_value); + + const FieldValue& item13 = event1.getValues()[13]; + EXPECT_EQ(0x2010784, item13.mField.getField()); + EXPECT_EQ(Type::FLOAT, item13.mValue.getType()); + EXPECT_EQ(2.2f, item13.mValue.float_value); + + const FieldValue& item14 = event1.getValues()[14]; + EXPECT_EQ(0x2018801, item14.mField.getField()); + EXPECT_EQ(Type::INT, item14.mValue.getType()); + EXPECT_EQ(222, item14.mValue.int_value); + + const FieldValue& item15 = event1.getValues()[15]; + EXPECT_EQ(0x2018884, item15.mField.getField()); + EXPECT_EQ(Type::FLOAT, item15.mValue.getType()); + EXPECT_EQ(1.1f, item15.mValue.float_value); } TEST(LogEventTest, TestLogParsing2) { @@ -242,12 +269,16 @@ TEST(LogEventTest, TestLogParsing2) { } TEST(LogEventTest, TestKeyValuePairsEvent) { - std::map<int32_t, int64_t> int_map; + std::map<int32_t, int32_t> int_map; + std::map<int32_t, int64_t> long_map; std::map<int32_t, std::string> string_map; std::map<int32_t, float> float_map; - int_map[11] = 123L; - int_map[22] = 345L; + int_map[11] = 123; + int_map[22] = 345; + + long_map[33] = 678L; + long_map[44] = 890L; string_map[1] = "test2"; string_map[2] = "test1"; @@ -255,7 +286,7 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { float_map[111] = 2.2f; float_map[222] = 1.1f; - LogEvent event1(83, 2000, 2001, 10001, int_map, string_map, float_map); + LogEvent event1(83, 2000, 2001, 10001, int_map, long_map, string_map, float_map); event1.init(); EXPECT_EQ(83, event1.GetTagId()); @@ -263,7 +294,7 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { EXPECT_EQ((int64_t)2001, event1.GetElapsedTimestampNs()); const auto& items = event1.getValues(); - EXPECT_EQ((size_t)13, items.size()); + EXPECT_EQ((size_t)17, items.size()); const FieldValue& item0 = event1.getValues()[0]; EXPECT_EQ(0x00010000, item0.mField.getField()); @@ -277,8 +308,8 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { const FieldValue& item2 = event1.getValues()[2]; EXPECT_EQ(0x2020182, item2.mField.getField()); - EXPECT_EQ(Type::LONG, item2.mValue.getType()); - EXPECT_EQ(123L, item2.mValue.long_value); + EXPECT_EQ(Type::INT, item2.mValue.getType()); + EXPECT_EQ(123, item2.mValue.int_value); const FieldValue& item3 = event1.getValues()[3]; EXPECT_EQ(0x2020201, item3.mField.getField()); @@ -287,48 +318,68 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { const FieldValue& item4 = event1.getValues()[4]; EXPECT_EQ(0x2020282, item4.mField.getField()); - EXPECT_EQ(Type::LONG, item4.mValue.getType()); - EXPECT_EQ(345L, item4.mValue.long_value); + EXPECT_EQ(Type::INT, item4.mValue.getType()); + EXPECT_EQ(345, item4.mValue.int_value); const FieldValue& item5 = event1.getValues()[5]; EXPECT_EQ(0x2020301, item5.mField.getField()); EXPECT_EQ(Type::INT, item5.mValue.getType()); - EXPECT_EQ(1, item5.mValue.int_value); + EXPECT_EQ(33, item5.mValue.int_value); const FieldValue& item6 = event1.getValues()[6]; - EXPECT_EQ(0x2020383, item6.mField.getField()); - EXPECT_EQ(Type::STRING, item6.mValue.getType()); - EXPECT_EQ("test2", item6.mValue.str_value); + EXPECT_EQ(0x2020382, item6.mField.getField()); + EXPECT_EQ(Type::LONG, item6.mValue.getType()); + EXPECT_EQ(678L, item6.mValue.long_value); const FieldValue& item7 = event1.getValues()[7]; EXPECT_EQ(0x2020401, item7.mField.getField()); EXPECT_EQ(Type::INT, item7.mValue.getType()); - EXPECT_EQ(2, item7.mValue.int_value); + EXPECT_EQ(44, item7.mValue.int_value); const FieldValue& item8 = event1.getValues()[8]; - EXPECT_EQ(0x2020483, item8.mField.getField()); - EXPECT_EQ(Type::STRING, item8.mValue.getType()); - EXPECT_EQ("test1", item8.mValue.str_value); + EXPECT_EQ(0x2020482, item8.mField.getField()); + EXPECT_EQ(Type::LONG, item8.mValue.getType()); + EXPECT_EQ(890L, item8.mValue.long_value); const FieldValue& item9 = event1.getValues()[9]; EXPECT_EQ(0x2020501, item9.mField.getField()); EXPECT_EQ(Type::INT, item9.mValue.getType()); - EXPECT_EQ(111, item9.mValue.int_value); + EXPECT_EQ(1, item9.mValue.int_value); const FieldValue& item10 = event1.getValues()[10]; - EXPECT_EQ(0x2020584, item10.mField.getField()); - EXPECT_EQ(Type::FLOAT, item10.mValue.getType()); - EXPECT_EQ(2.2f, item10.mValue.float_value); + EXPECT_EQ(0x2020583, item10.mField.getField()); + EXPECT_EQ(Type::STRING, item10.mValue.getType()); + EXPECT_EQ("test2", item10.mValue.str_value); const FieldValue& item11 = event1.getValues()[11]; - EXPECT_EQ(0x2028601, item11.mField.getField()); + EXPECT_EQ(0x2020601, item11.mField.getField()); EXPECT_EQ(Type::INT, item11.mValue.getType()); - EXPECT_EQ(222, item11.mValue.int_value); + EXPECT_EQ(2, item11.mValue.int_value); const FieldValue& item12 = event1.getValues()[12]; - EXPECT_EQ(0x2028684, item12.mField.getField()); - EXPECT_EQ(Type::FLOAT, item12.mValue.getType()); - EXPECT_EQ(1.1f, item12.mValue.float_value); + EXPECT_EQ(0x2020683, item12.mField.getField()); + EXPECT_EQ(Type::STRING, item12.mValue.getType()); + EXPECT_EQ("test1", item12.mValue.str_value); + + const FieldValue& item13 = event1.getValues()[13]; + EXPECT_EQ(0x2020701, item13.mField.getField()); + EXPECT_EQ(Type::INT, item13.mValue.getType()); + EXPECT_EQ(111, item13.mValue.int_value); + + const FieldValue& item14 = event1.getValues()[14]; + EXPECT_EQ(0x2020784, item14.mField.getField()); + EXPECT_EQ(Type::FLOAT, item14.mValue.getType()); + EXPECT_EQ(2.2f, item14.mValue.float_value); + + const FieldValue& item15 = event1.getValues()[15]; + EXPECT_EQ(0x2028801, item15.mField.getField()); + EXPECT_EQ(Type::INT, item15.mValue.getType()); + EXPECT_EQ(222, item15.mValue.int_value); + + const FieldValue& item16 = event1.getValues()[16]; + EXPECT_EQ(0x2028884, item16.mField.getField()); + EXPECT_EQ(Type::FLOAT, item16.mValue.getType()); + EXPECT_EQ(1.1f, item16.mValue.float_value); } @@ -337,4 +388,4 @@ TEST(LogEventTest, TestKeyValuePairsEvent) { } // namespace android #else GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif
\ No newline at end of file +#endif 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 f03821432cc1..75bd40f67946 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 @@ StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition( } // 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 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi 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 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi 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 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi 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 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi 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/Notification.java b/core/java/android/app/Notification.java index 3638bc48d2b5..81df447816d1 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4440,7 +4440,7 @@ public class Notification implements Parcelable } private CharSequence processTextSpans(CharSequence text) { - if (hasForegroundColor()) { + if (hasForegroundColor() || mInNightMode) { return ContrastColorUtil.clearColorSpans(text); } return text; diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 01557c59f8ac..eb5c720d6309 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -649,7 +649,7 @@ public final class SQLiteDatabase extends SQLiteClosable { * successful so far. Do not call setTransactionSuccessful before calling this. When this * returns a new transaction will have been created but not marked as successful. * @return true if the transaction was yielded - * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock + * @deprecated if the db is locked more than once (because of nested transactions) then the lock * will not be yielded. Use yieldIfContendedSafely instead. */ @Deprecated diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 92a814ca24c5..83998cc1c66a 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -348,7 +348,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * @hide */ public AuthenticationResult(CryptoObject crypto) { - // For compatibility, this extends from common base class as FingerprintManager does. // Identifier and userId is not used for BiometricPrompt. super(crypto, null /* identifier */, 0 /* userId */); } @@ -410,8 +409,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** - * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts - * scanning for a fingerprint. It terminates when {@link + * This call warms up the biometric hardware, displays a system-provided dialog, and starts + * scanning for a biometric. It terminates when {@link * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)}, or when the user * dismisses the system-provided dialog, at which point the crypto object becomes invalid. This @@ -453,8 +452,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** - * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts - * scanning for a fingerprint. It terminates when {@link + * This call warms up the biometric hardware, displays a system-provided dialog, and starts + * scanning for a biometric. It terminates when {@link * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)} is called, or when * the user dismisses the system-provided dialog. This operation can be canceled by using the diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 66613ea50357..873a24a3b53b 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -244,17 +244,17 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** - * Requests a pre-enrollment auth token to tie enrollment to the confirmation of + * Requests an auth token to tie sensitive operations to the confirmation of * existing device credentials (e.g. pin/pattern/password). * * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) - public long preEnroll() { + public long generateChallenge() { long result = 0; if (mService != null) { try { - result = mService.preEnroll(mToken); + result = mService.generateChallenge(mToken); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -263,16 +263,46 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** - * Finishes enrollment and cancels the current auth token. + * Invalidates the current auth token. * * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) - public int postEnroll() { + public int revokeChallenge() { int result = 0; if (mService != null) { try { - result = mService.postEnroll(mToken); + result = mService.revokeChallenge(mToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return result; + } + + /** + * @hide + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public void setRequireAttention(boolean requireAttention, byte[] token) { + if (mService != null) { + try { + mService.setRequireAttention(requireAttention, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * @hide + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public boolean getRequireAttention(byte[] token) { + boolean result = true; + if (mService != null) { + try { + mService.getRequireAttention(token); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 50d07449493e..6681bd714779 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -66,10 +66,10 @@ interface IFaceService { boolean isHardwareDetected(long deviceId, String opPackageName); // Get a pre-enrollment authentication token - long preEnroll(IBinder token); + long generateChallenge(IBinder token); // Finish an enrollment sequence and invalidate the authentication token - int postEnroll(IBinder token); + int revokeChallenge(IBinder token); // Determine if a user has at least one enrolled face boolean hasEnrolledFaces(int userId, String opPackageName); @@ -94,4 +94,8 @@ interface IFaceService { // Enumerate all faces void enumerate(IBinder token, int userId, IFaceServiceReceiver receiver); + + int setRequireAttention(boolean requireAttention, in byte [] token); + + boolean getRequireAttention(in byte [] token); } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index f2e907833612..8333b817add0 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -26,7 +26,6 @@ import android.annotation.UnsupportedAppUsage; 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 @@ public class ConnectivityManager { 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 @@ public class ConnectivityManager { // 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 6bd2e76cdf35..8681893702b4 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -30,6 +30,8 @@ import com.android.internal.telephony.TelephonyProperties; import dalvik.system.VMRuntime; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -1083,7 +1085,67 @@ public class Build { 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 f2e0bddb93aa..54be6393e651 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -44,6 +44,8 @@ public class GraphicsEnvironment { private static final boolean DEBUG = false; 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; @@ -54,6 +56,7 @@ public class GraphicsEnvironment { */ public void setup(Context context) { setupGpuLayers(context); + setupAngle(context); chooseDriver(context); } @@ -121,7 +124,6 @@ public class GraphicsEnvironment { } } } - } // Include the app's lib directory in all cases @@ -131,6 +133,80 @@ public class GraphicsEnvironment { } /** + * Pass ANGLE details down to trigger enable logic + */ + private static void setupAngle(Context context) { + + String angleEnabledApp = + Settings.Global.getString(context.getContentResolver(), + Settings.Global.ANGLE_ENABLED_APP); + + String packageName = context.getPackageName(); + + boolean devOptIn = false; + if ((angleEnabledApp != null && packageName != null) + && (!angleEnabledApp.isEmpty() && !packageName.isEmpty()) + && angleEnabledApp.equals(packageName)) { + + if (DEBUG) Log.v(TAG, packageName + " opted in for ANGLE via Developer Setting"); + + 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); + } + + /** * Choose whether the current process should use the builtin or an updated driver. */ private static void chooseDriver(Context context) { @@ -218,4 +294,6 @@ public class GraphicsEnvironment { 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 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 6fab3c412ae5..0f64c4531bc3 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -483,6 +483,8 @@ public class Process { * @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 @@ public class Process { @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 @@ public class Process { @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 99181acb03c7..7fd0a4b66d66 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -215,6 +215,8 @@ public class ZygoteProcess { * @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 @@ public class ZygoteProcess { @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 @@ public class ZygoteProcess { * @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 @@ public class ZygoteProcess { @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 @@ public class ZygoteProcess { 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 @@ public class ZygoteProcess { 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 d850e27e913f..1f54ea53facc 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -89,8 +89,13 @@ public abstract class StorageManagerInternal { * @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 afd383691300..e55afb69bab9 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -157,7 +157,7 @@ public class VolumeInfo implements Parcelable { 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/Settings.java b/core/java/android/provider/Settings.java index aa178fb9ff36..1d3cf19e18f0 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7267,12 +7267,12 @@ public final class Settings { 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. @@ -7689,6 +7689,15 @@ public final class Settings { BOOLEAN_VALIDATOR; /** + * Whether or not face unlock is allowed for apps (through BiometricPrompt). + * @hide + */ + public static final String FACE_UNLOCK_APP_ENABLED = "face_unlock_app_enabled"; + + private static final Validator FACE_UNLOCK_APP_ENABLED_VALIDATOR = + BOOLEAN_VALIDATOR; + + /** * Whether the assist gesture should be enabled. * * @hide @@ -8184,11 +8193,12 @@ public final class Settings { 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, FACE_UNLOCK_KEYGUARD_ENABLED, + FACE_UNLOCK_APP_ENABLED, ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, ASSIST_GESTURE_WAKE_ENABLED, @@ -8331,12 +8341,13 @@ public final class Settings { 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, AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR); VALIDATORS.put(FACE_UNLOCK_KEYGUARD_ENABLED, FACE_UNLOCK_KEYGUARD_ENABLED_VALIDATOR); + VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR); VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR); VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR); @@ -9593,8 +9604,7 @@ public final class Settings { * 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 = @@ -11570,6 +11580,12 @@ public final class Settings { public static final String GPU_DEBUG_APP = "gpu_debug_app"; /** + * App should try to use ANGLE + * @hide + */ + public static final String ANGLE_ENABLED_APP = "angle_enabled_app"; + + /** * Ordered GPU debug layer list * i.e. <layer1>:<layer2>:...:<layerN> * @hide diff --git a/core/java/android/text/NativeLineBreaker.java b/core/java/android/text/NativeLineBreaker.java index 2bcfa5fe0857..94e10e89acbd 100644 --- a/core/java/android/text/NativeLineBreaker.java +++ b/core/java/android/text/NativeLineBreaker.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; @@ -258,16 +259,91 @@ public class NativeLineBreaker { /** * A result object of a line breaking */ - public static class LineBreaks { - public int breakCount; - private static final int INITIAL_SIZE = 16; - public int[] breaks = new int[INITIAL_SIZE]; - public float[] widths = new float[INITIAL_SIZE]; - public float[] ascents = new float[INITIAL_SIZE]; - public float[] descents = new float[INITIAL_SIZE]; - // TODO: Introduce Hyphenator for explaining the meaning of flags. - public int[] flags = new int[INITIAL_SIZE]; - // breaks, widths, and flags should all have the same length + public static class Result { + // Following two contstant must be synced with minikin's line breaker. + private static final int TAB_MASK = 0x20000000; + private static final int HYPHEN_MASK = 0xFF; + + private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( + Result.class.getClassLoader(), nGetReleaseResultFunc(), 32); + private final long mPtr; + + private Result(long ptr) { + mPtr = ptr; + sRegistry.registerNativeAllocation(this, mPtr); + } + + /** + * Returns a number of line count. + * + * @return number of lines + */ + public @IntRange(from = 0) int getLineCount() { + return nGetLineCount(mPtr); + } + + /** + * Returns a break offset of the line. + * + * @param lineIndex an index of the line. + * @return the break offset. + */ + public @IntRange(from = 0) int getLineBreakOffset(@IntRange(from = 0) int lineIndex) { + return nGetLineBreakOffset(mPtr, lineIndex); + } + + /** + * Returns a width of the line in pixels. + * + * @param lineIndex an index of the line. + * @return a width of the line in pixexls + */ + public @Px float getLineWidth(@IntRange(from = 0) int lineIndex) { + return nGetLineWidth(mPtr, lineIndex); + } + + /** + * Returns an entier font ascent of the line in pixels. + * + * @param lineIndex an index of the line. + * @return an entier font ascent of the line in pixels. + */ + public @Px float getLineAscent(@IntRange(from = 0) int lineIndex) { + return nGetLineAscent(mPtr, lineIndex); + } + + /** + * Returns an entier font descent of the line in pixels. + * + * @param lineIndex an index of the line. + * @return an entier font descent of the line in pixels. + */ + public @Px float getLineDescent(@IntRange(from = 0) int lineIndex) { + return nGetLineDescent(mPtr, lineIndex); + } + + /** + * Returns true if the line has a TAB character. + * + * @param lineIndex an index of the line. + * @return true if the line has a TAB character + */ + public boolean hasLineTab(int lineIndex) { + return (nGetLineFlag(mPtr, lineIndex) & TAB_MASK) != 0; + } + + /** + * Returns a packed packed hyphen edit for the line. + * + * @param lineIndex an index of the line. + * @return a packed hyphen edit for the line. + * @see android.text.Hyphenator#unpackStartHyphenEdit(int) + * @see android.text.Hyphenator#unpackEndHyphenEdit(int) + * @see android.text.Hyphenator#packHyphenEdit(int,int) + */ + public int getLineHyphenEdit(int lineIndex) { + return (nGetLineFlag(mPtr, lineIndex) & HYPHEN_MASK); + } } private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( @@ -294,14 +370,12 @@ public class NativeLineBreaker { * @param measuredPara a result of the text measurement * @param constraints for a single paragraph * @param lineNumber a line number of this paragraph - * @param out object to set line break information for the given paragraph */ - public void computeLineBreaks( + public Result computeLineBreaks( @NonNull NativeMeasuredParagraph measuredPara, @NonNull ParagraphConstraints constraints, - @IntRange(from = 0) int lineNumber, - @NonNull LineBreaks out) { - out.breakCount = nComputeLineBreaks( + @IntRange(from = 0) int lineNumber) { + return new Result(nComputeLineBreaks( mNativePtr, // Inputs @@ -313,17 +387,7 @@ public class NativeLineBreaker { constraints.mWidth, constraints.mVariableTabStops, constraints.mDefaultTabStop, - lineNumber, - - // Outputs - out, - out.breaks.length, - out.breaks, - out.widths, - out.ascents, - out.descents, - out.flags); - + lineNumber)); } @FastNative @@ -341,7 +405,7 @@ public class NativeLineBreaker { // arrays do not have to be resized // The individual character widths will be returned in charWidths. The length of // charWidths must be at least the length of the text. - private static native int nComputeLineBreaks( + private static native long nComputeLineBreaks( /* non zero */ long nativePtr, // Inputs @@ -353,14 +417,21 @@ public class NativeLineBreaker { @FloatRange(from = 0.0f) float restWidth, @Nullable int[] variableTabStops, int defaultTabStop, - @IntRange(from = 0) int indentsOffset, - - // Outputs - @NonNull LineBreaks recycle, - @IntRange(from = 0) int recycleLength, - @NonNull int[] recycleBreaks, - @NonNull float[] recycleWidths, - @NonNull float[] recycleAscents, - @NonNull float[] recycleDescents, - @NonNull int[] recycleFlags); + @IntRange(from = 0) int indentsOffset); + + // Result accessors + @CriticalNative + private static native int nGetLineCount(long ptr); + @CriticalNative + private static native int nGetLineBreakOffset(long ptr, int idx); + @CriticalNative + private static native float nGetLineWidth(long ptr, int idx); + @CriticalNative + private static native float nGetLineAscent(long ptr, int idx); + @CriticalNative + private static native float nGetLineDescent(long ptr, int idx); + @CriticalNative + private static native int nGetLineFlag(long ptr, int idx); + @CriticalNative + private static native long nGetReleaseResultFunc(); } diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index c5fabaf2d840..0d29da00f7c9 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -1610,13 +1610,14 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable public boolean equals(Object o) { if (o instanceof Spanned && toString().equals(o.toString())) { - Spanned other = (Spanned) o; + final Spanned other = (Spanned) o; // Check span data - Object[] otherSpans = other.getSpans(0, other.length(), Object.class); + final Object[] otherSpans = other.getSpans(0, other.length(), Object.class); + final Object[] thisSpans = getSpans(0, length(), Object.class); if (mSpanCount == otherSpans.length) { for (int i = 0; i < mSpanCount; ++i) { - Object thisSpan = mSpans[i]; - Object otherSpan = otherSpans[i]; + final Object thisSpan = thisSpans[i]; + final Object otherSpan = otherSpans[i]; if (thisSpan == this) { if (other != otherSpan || getSpanStart(thisSpan) != other.getSpanStart(otherSpan) || diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 7acd5399792c..a9866335f271 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -16,12 +16,13 @@ package android.text; +import android.annotation.UnsupportedAppUsage; + import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; import libcore.util.EmptyArray; -import android.annotation.UnsupportedAppUsage; import java.lang.reflect.Array; /* package */ abstract class SpannableStringInternal @@ -502,13 +503,14 @@ import java.lang.reflect.Array; public boolean equals(Object o) { if (o instanceof Spanned && toString().equals(o.toString())) { - Spanned other = (Spanned) o; + final Spanned other = (Spanned) o; // Check span data - Object[] otherSpans = other.getSpans(0, other.length(), Object.class); + final Object[] otherSpans = other.getSpans(0, other.length(), Object.class); + final Object[] thisSpans = getSpans(0, length(), Object.class); if (mSpanCount == otherSpans.length) { for (int i = 0; i < mSpanCount; ++i) { - Object thisSpan = mSpans[i]; - Object otherSpan = otherSpans[i]; + final Object thisSpan = thisSpans[i]; + final Object otherSpan = otherSpans[i]; if (thisSpan == this) { if (other != otherSpan || getSpanStart(thisSpan) != other.getSpanStart(otherSpan) || diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index e1ffef01feae..d2f085369448 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -599,7 +599,14 @@ public class StaticLayout extends Layout { float ellipsizedWidth = b.mEllipsizedWidth; TextUtils.TruncateAt ellipsize = b.mEllipsize; final boolean addLastLineSpacing = b.mAddLastLineLineSpacing; - NativeLineBreaker.LineBreaks lineBreaks = new NativeLineBreaker.LineBreaks(); + + int lineBreakCapacity = 0; + int[] breaks = null; + float[] lineWidths = null; + float[] ascents = null; + float[] descents = null; + boolean[] hasTabs = null; + int[] hyphenEdits = null; mLineCount = 0; mEllipsized = false; @@ -732,14 +739,27 @@ public class StaticLayout extends Layout { constraints.setIndent(firstWidth, firstWidthLineCount); constraints.setTabStops(variableTabStops, TAB_INCREMENT); - lineBreaker.computeLineBreaks(measuredPara.getNativeMeasuredParagraph(), - constraints, mLineCount, lineBreaks); - int breakCount = lineBreaks.breakCount; - final int[] breaks = lineBreaks.breaks; - final float[] lineWidths = lineBreaks.widths; - final float[] ascents = lineBreaks.ascents; - final float[] descents = lineBreaks.descents; - final int[] flags = lineBreaks.flags; + NativeLineBreaker.Result res = lineBreaker.computeLineBreaks( + measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount); + int breakCount = res.getLineCount(); + if (lineBreakCapacity < breakCount) { + lineBreakCapacity = breakCount; + breaks = new int[lineBreakCapacity]; + lineWidths = new float[lineBreakCapacity]; + ascents = new float[lineBreakCapacity]; + descents = new float[lineBreakCapacity]; + hasTabs = new boolean[lineBreakCapacity]; + hyphenEdits = new int[lineBreakCapacity]; + } + + for (int i = 0; i < breakCount; ++i) { + breaks[i] = res.getLineBreakOffset(i); + lineWidths[i] = res.getLineWidth(i); + ascents[i] = res.getLineAscent(i); + descents[i] = res.getLineDescent(i); + hasTabs[i] = res.hasLineTab(i); + hyphenEdits[i] = res.getLineHyphenEdit(i); + } final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; final boolean ellipsisMayBeApplied = ellipsize != null @@ -750,7 +770,7 @@ public class StaticLayout extends Layout { && ellipsisMayBeApplied) { // Calculate width float width = 0; - int flag = 0; // XXX May need to also have starting hyphen edit + boolean hasTab = false; // XXX May need to also have starting hyphen edit for (int i = remainingLineCount - 1; i < breakCount; i++) { if (i == breakCount - 1) { width += lineWidths[i]; @@ -759,12 +779,12 @@ public class StaticLayout extends Layout { width += measuredPara.getCharWidthAt(j - paraStart); } } - flag |= flags[i] & TAB_MASK; + hasTab |= hasTabs[i]; } // Treat the last line and overflowed lines as a single line. breaks[remainingLineCount - 1] = breaks[breakCount - 1]; lineWidths[remainingLineCount - 1] = width; - flags[remainingLineCount - 1] = flag; + hasTabs[remainingLineCount - 1] = hasTab; breakCount = remainingLineCount; } @@ -821,8 +841,8 @@ public class StaticLayout extends Layout { v = out(source, here, endPos, ascent, descent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, - flags[breakIndex], needMultiply, measuredPara, bufEnd, - includepad, trackpad, addLastLineSpacing, chs, + hasTabs[breakIndex], hyphenEdits[breakIndex], needMultiply, + measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars); @@ -860,7 +880,7 @@ public class StaticLayout extends Layout { fm.top, fm.bottom, v, spacingmult, spacingadd, null, - null, fm, 0, + null, fm, false, 0, needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, null, bufStart, ellipsize, @@ -871,7 +891,8 @@ public class StaticLayout extends Layout { private int out(final CharSequence text, final int start, final int end, int above, int below, int top, int bottom, int v, final float spacingmult, final float spacingadd, final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm, - final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured, + final boolean hasTab, final int hyphenEdit, final boolean needMultiply, + @NonNull final MeasuredParagraph measured, final int bufEnd, final boolean includePad, final boolean trackPad, final boolean addLastLineLineSpacing, final char[] chs, final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth, @@ -1005,8 +1026,8 @@ public class StaticLayout extends Layout { // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining // one bit for start field - lines[off + TAB] |= flags & TAB_MASK; - lines[off + HYPHEN] = flags; + lines[off + TAB] |= hasTab ? TAB_MASK : 0; + lines[off + HYPHEN] = hyphenEdit; lines[off + DIR] |= dir << DIR_SHIFT; mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart); diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java index c17cfd500827..2dc4f6001a06 100644 --- a/core/java/android/text/style/TextAppearanceSpan.java +++ b/core/java/android/text/style/TextAppearanceSpan.java @@ -16,11 +16,13 @@ package android.text.style; +import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.LeakyTypefaceStorage; import android.graphics.Typeface; +import android.graphics.fonts.Font; import android.os.Parcel; import android.text.ParcelableSpan; import android.text.TextPaint; @@ -38,6 +40,21 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl private final ColorStateList mTextColorLink; private final Typeface mTypeface; + private final int mTextFontWeight; + + private final float mShadowRadius; + private final float mShadowDx; + private final float mShadowDy; + private final int mShadowColor; + + private final boolean mHasElegantTextHeight; + private final boolean mElegantTextHeight; + private final boolean mHasLetterSpacing; + private final float mLetterSpacing; + + private final String mFontFeatureSettings; + private final String mFontVariationSettings; + /** * Uses the specified TextAppearance resource to determine the * text appearance. The <code>appearance</code> should be, for example, @@ -104,6 +121,34 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl } } + mTextFontWeight = a.getInt(com.android.internal.R.styleable + .TextAppearance_textFontWeight, -1); + + mShadowRadius = a.getFloat(com.android.internal.R.styleable + .TextAppearance_shadowRadius, 0.0f); + mShadowDx = a.getFloat(com.android.internal.R.styleable + .TextAppearance_shadowDx, 0.0f); + mShadowDy = a.getFloat(com.android.internal.R.styleable + .TextAppearance_shadowDy, 0.0f); + mShadowColor = a.getInt(com.android.internal.R.styleable + .TextAppearance_shadowColor, 0); + + mHasElegantTextHeight = a.hasValue(com.android.internal.R.styleable + .TextAppearance_elegantTextHeight); + mElegantTextHeight = a.getBoolean(com.android.internal.R.styleable + .TextAppearance_elegantTextHeight, false); + + mHasLetterSpacing = a.hasValue(com.android.internal.R.styleable + .TextAppearance_letterSpacing); + mLetterSpacing = a.getFloat(com.android.internal.R.styleable + .TextAppearance_letterSpacing, 0.0f); + + mFontFeatureSettings = a.getString(com.android.internal.R.styleable + .TextAppearance_fontFeatureSettings); + + mFontVariationSettings = a.getString(com.android.internal.R.styleable + .TextAppearance_fontVariationSettings); + a.recycle(); if (colorList >= 0) { @@ -129,6 +174,21 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl mTextColor = color; mTextColorLink = linkColor; mTypeface = null; + + mTextFontWeight = -1; + + mShadowRadius = 0.0f; + mShadowDx = 0.0f; + mShadowDy = 0.0f; + mShadowColor = 0; + + mHasElegantTextHeight = false; + mElegantTextHeight = false; + mHasLetterSpacing = false; + mLetterSpacing = 0.0f; + + mFontFeatureSettings = null; + mFontVariationSettings = null; } public TextAppearanceSpan(Parcel src) { @@ -146,6 +206,21 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl mTextColorLink = null; } mTypeface = LeakyTypefaceStorage.readTypefaceFromParcel(src); + + mTextFontWeight = src.readInt(); + + mShadowRadius = src.readFloat(); + mShadowDx = src.readFloat(); + mShadowDy = src.readFloat(); + mShadowColor = src.readInt(); + + mHasElegantTextHeight = src.readBoolean(); + mElegantTextHeight = src.readBoolean(); + mHasLetterSpacing = src.readBoolean(); + mLetterSpacing = src.readFloat(); + + mFontFeatureSettings = src.readString(); + mFontVariationSettings = src.readString(); } public int getSpanTypeId() { @@ -183,6 +258,21 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl dest.writeInt(0); } LeakyTypefaceStorage.writeTypefaceToParcel(mTypeface, dest); + + dest.writeInt(mTextFontWeight); + + dest.writeFloat(mShadowRadius); + dest.writeFloat(mShadowDx); + dest.writeFloat(mShadowDy); + dest.writeInt(mShadowColor); + + dest.writeBoolean(mHasElegantTextHeight); + dest.writeBoolean(mElegantTextHeight); + dest.writeBoolean(mHasLetterSpacing); + dest.writeFloat(mLetterSpacing); + + dest.writeString(mFontFeatureSettings); + dest.writeString(mFontVariationSettings); } /** @@ -225,6 +315,81 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl return mStyle; } + /** + * Returns the text font weight specified by this span, or <code>-1</code> + * if it does not specify one. + */ + public int getTextFontWeight() { + return mTextFontWeight; + } + + /** + * Returns the typeface specified by this span, or <code>null</code> + * if it does not specify one. + */ + @Nullable + public Typeface getTypeface() { + return mTypeface; + } + + /** + * Returns the color of the text shadow specified by this span, or <code>0</code> + * if it does not specify one. + */ + public int getShadowColor() { + return mShadowColor; + } + + /** + * Returns the horizontal offset of the text shadow specified by this span, or <code>0.0f</code> + * if it does not specify one. + */ + public float getShadowDx() { + return mShadowDx; + } + + /** + * Returns the vertical offset of the text shadow specified by this span, or <code>0.0f</code> + * if it does not specify one. + */ + public float getShadowDy() { + return mShadowDy; + } + + /** + * Returns the blur radius of the text shadow specified by this span, or <code>0.0f</code> + * if it does not specify one. + */ + public float getShadowRadius() { + return mShadowRadius; + } + + /** + * Returns the font feature settings specified by this span, or <code>null</code> + * if it does not specify one. + */ + @Nullable + public String getFontFeatureSettings() { + return mFontFeatureSettings; + } + + /** + * Returns the font variation settings specified by this span, or <code>null</code> + * if it does not specify one. + */ + @Nullable + public String getFontVariationSettings() { + return mFontVariationSettings; + } + + /** + * Returns the value of elegant height metrics flag specified by this span, + * or <code>false</code> if it does not specify one. + */ + public boolean isElegantTextHeight() { + return mElegantTextHeight; + } + @Override public void updateDrawState(TextPaint ds) { updateMeasureState(ds); @@ -236,6 +401,10 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl if (mTextColorLink != null) { ds.linkColor = mTextColorLink.getColorForState(ds.drawableState, 0); } + + if (mShadowColor != 0) { + ds.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor); + } } @Override @@ -267,7 +436,16 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl } if (styledTypeface != null) { - int fake = style & ~styledTypeface.getStyle(); + final Typeface readyTypeface; + if (mTextFontWeight >= 0) { + final int weight = Math.min(Font.FONT_WEIGHT_MAX, mTextFontWeight); + final boolean italic = (style & Typeface.ITALIC) != 0; + readyTypeface = ds.setTypeface(Typeface.create(styledTypeface, weight, italic)); + } else { + readyTypeface = styledTypeface; + } + + int fake = style & ~readyTypeface.getStyle(); if ((fake & Typeface.BOLD) != 0) { ds.setFakeBoldText(true); @@ -277,11 +455,27 @@ public class TextAppearanceSpan extends MetricAffectingSpan implements Parcelabl ds.setTextSkewX(-0.25f); } - ds.setTypeface(styledTypeface); + ds.setTypeface(readyTypeface); } if (mTextSize > 0) { ds.setTextSize(mTextSize); } + + if (mHasElegantTextHeight) { + ds.setElegantTextHeight(mElegantTextHeight); + } + + if (mHasLetterSpacing) { + ds.setLetterSpacing(mLetterSpacing); + } + + if (mFontFeatureSettings != null) { + ds.setFontFeatureSettings(mFontFeatureSettings); + } + + if (mFontVariationSettings != null) { + ds.setFontVariationSettings(mFontVariationSettings); + } } } diff --git a/core/java/android/transition/ChangeImageTransform.java b/core/java/android/transition/ChangeImageTransform.java index d7a912057858..9fa9961d21bc 100644 --- a/core/java/android/transition/ChangeImageTransform.java +++ b/core/java/android/transition/ChangeImageTransform.java @@ -97,22 +97,13 @@ public class ChangeImageTransform extends Transition { 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 @@ public class ChangeImageTransform extends Transition { } Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS); Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS); - if (startBounds == null || endBounds == null) { - return 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; + } - 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 @@ public class ChangeImageTransform extends Transition { 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 @@ public class ChangeImageTransform extends Transition { 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 c4ef77a3e7ae..4608e205ec7c 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -188,8 +188,12 @@ public class TransitionManager { 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 @@ public class TransitionManager { Transition transitionClone = transition.clone(); transitionClone.setSceneRoot(sceneRoot); - Scene oldScene = Scene.getCurrentScene(sceneRoot); if (oldScene != null && oldScene.isCreatedFromLayoutResource()) { transitionClone.setCanRemoveViews(true); } @@ -212,14 +215,6 @@ public class TransitionManager { } } - 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 @@ public class TransitionManager { 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 5108a796a036..294a1799a2d3 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -71,19 +71,19 @@ public final class ArrayMap<K, V> implements Map<K, V> { /** * 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 @@ public final class ArrayMap<K, V> implements Map<K, V> { * 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 @@ public final class ArrayMap<K, V> implements Map<K, V> { } } - @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 @@ public final class ArrayMap<K, V> implements Map<K, V> { return ~end; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null) int indexOfNull() { final int N = mSize; @@ -200,7 +200,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { 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 @@ public final class ArrayMap<K, V> implements Map<K, V> { 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 @@ public final class ArrayMap<K, V> implements Map<K, V> { : 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 @@ public final class ArrayMap<K, V> implements Map<K, V> { * 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 526a950b4820..d74a0fe8d2c1 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -71,15 +71,15 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { 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 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { return ~end; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null) private int indexOfNull() { final int N = mSize; @@ -157,7 +157,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { 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 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { 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 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { } } - /** {@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 d5af92234b31..af163ac8f246 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -46,11 +46,11 @@ import libcore.util.EmptyArray; * @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 aa5ca3530621..89ea2d35fc2f 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -56,11 +56,11 @@ public class SparseArray<E> implements Cloneable { 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 9c6b9698d1ae..d4c40954bdd1 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -185,7 +185,9 @@ public class SparseBooleanArray implements Cloneable { 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 @@ public class SparseBooleanArray implements Cloneable { 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 19547534aef5..9e6bad1d9ae0 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -46,11 +46,11 @@ import libcore.util.EmptyArray; * 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 @@ public class SparseIntArray implements Cloneable { /** * 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 1203541756e8..1bbef8e9cfff 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -410,7 +410,7 @@ public class ApkSignatureSchemeV2Verifier { 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 @@ public class ApkSignatureSchemeV2Verifier { 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 939522dcd57f..1471870bd7d2 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -534,7 +534,7 @@ public class ApkSignatureSchemeV3Verifier { 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 @@ public class ApkSignatureSchemeV3Verifier { 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 081033ae84e9..87af5364c945 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -332,7 +332,7 @@ final class ApkSigningBlockUtils { 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 index edd09f8f73c4..443bbd8597af 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/VerityBuilder.java @@ -29,19 +29,18 @@ import java.security.NoSuchAlgorithmException; 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 @@ public abstract class ApkVerityBuilder { 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 @@ public abstract class ApkVerityBuilder { /** 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 @@ public abstract class ApkVerityBuilder { * 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 @@ public abstract class ApkVerityBuilder { * 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 @@ public abstract class ApkVerityBuilder { 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 @@ public abstract class ApkVerityBuilder { 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/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 0986b89849cf..42690cef9da3 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -974,6 +974,25 @@ public final class ThreadedRenderer { } } + /** The root of everything */ + public @NonNull RenderNode getRootNode() { + return mRootNode; + } + + private boolean mForceDark = false; + + /** + * Whether or not the force-dark feature should be used for this renderer. + */ + public boolean setForceDark(boolean enable) { + if (mForceDark != enable) { + mForceDark = enable; + nSetForceDark(mNativeProxy, enable); + return true; + } + return false; + } + /** * Basic synchronous renderer. Currently only used to render the Magnifier, so use with care. * TODO: deduplicate against ThreadedRenderer. @@ -1253,4 +1272,5 @@ public final class ThreadedRenderer { private static native void nSetIsolatedProcess(boolean enabled); private static native void nSetContextPriority(int priority); private static native void nAllocateBuffers(long nativeProxy, Surface window); + private static native void nSetForceDark(long nativeProxy, boolean enabled); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1eb35c546c99..78e6dd8fb139 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15255,6 +15255,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * a value of 'true' will not override any 'false' value in its parent chain nor will * it prevent any 'false' in any of its children. * + * The default behavior of force dark is also influenced by the Theme's + * {@link android.R.styleable#Theme_isLightTheme isLightTheme} attribute. + * If a theme is isLightTheme="false", then force dark is globally disabled for that theme. + * * @param allow Whether or not to allow force dark. * * @hide diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 5bc44ce2bd49..bef8e8fedfdf 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -43,6 +43,7 @@ import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; @@ -1077,6 +1078,7 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent, attrs.getTitle().toString()); mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut); + updateForceDarkMode(); if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mHardwareAccelerated = mAttachInfo.mHardwareAccelerationRequested = true; @@ -1085,6 +1087,27 @@ public final class ViewRootImpl implements ViewParent, } } + private int getNightMode() { + return mContext.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + } + + private void updateForceDarkMode() { + if (mAttachInfo.mThreadedRenderer == null) return; + + boolean nightMode = getNightMode() == Configuration.UI_MODE_NIGHT_YES; + TypedArray a = mContext.obtainStyledAttributes(R.styleable.Theme); + boolean isLightTheme = a.getBoolean(R.styleable.Theme_isLightTheme, false); + a.recycle(); + + boolean changed = mAttachInfo.mThreadedRenderer.setForceDark(nightMode); + changed |= mAttachInfo.mThreadedRenderer.getRootNode().setAllowForceDark(isLightTheme); + + if (changed) { + // TODO: Don't require regenerating all display lists to apply this setting + invalidateWorld(mView); + } + } + @UnsupportedAppUsage public View getView() { return mView; @@ -4077,6 +4100,8 @@ public final class ViewRootImpl implements ViewParent, mForceNextWindowRelayout = true; requestLayout(); } + + updateForceDarkMode(); } /** diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index e1600c4a92c8..d09323d3f8ad 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -124,7 +124,8 @@ public interface InputMethod { * @hide */ @MainThread - public void updateInputMethodDisplay(int displayId); + default void updateInputMethodDisplay(int displayId) { + } /** * Bind a new application environment in to the input method, so that it diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 0fef9a54a2ff..12cc54d7241e 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -1338,7 +1338,9 @@ public class ImageView extends View { 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 cb282b69845c..0080ace230a2 100644 --- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java +++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java @@ -66,13 +66,13 @@ public class AmbientDisplayConfiguration { 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 @@ public class AmbientDisplayConfiguration { 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 927322e97e28..98b7b5d28779 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -133,15 +133,16 @@ public final class Zygote { * 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 @@ public final class Zygote { } 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 06c41d858f7c..4a94ec4a4071 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -241,7 +241,8 @@ class ZygoteConnection { 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 @@ class ZygoteConnection { /** 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 @@ class ZygoteConnection { 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/opengl/util.cpp b/core/jni/android/opengl/util.cpp index b16359727e40..a45b4933a900 100644 --- a/core/jni/android/opengl/util.cpp +++ b/core/jni/android/opengl/util.cpp @@ -622,13 +622,17 @@ void util_multiplyMV(JNIEnv *env, jclass clazz, // --------------------------------------------------------------------------- +// The internal format is no longer the same as pixel format, per Table 2 in +// https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml static int checkInternalFormat(SkColorType colorType, int internalformat, int type) { switch(colorType) { case kN32_SkColorType: return (type == GL_UNSIGNED_BYTE && - internalformat == GL_RGBA) ? 0 : -1; + internalformat == GL_RGBA) || + (type == GL_UNSIGNED_BYTE && + internalformat == GL_SRGB8_ALPHA8) ? 0 : -1; case kAlpha_8_SkColorType: return (type == GL_UNSIGNED_BYTE && internalformat == GL_ALPHA) ? 0 : -1; diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index dfa5de6b65c6..b70485d9a0ea 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -28,6 +28,14 @@ void setDriverPath(JNIEnv* env, jobject clazz, jstring path) { android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str()); } +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn) { + ScopedUtfChars pathChars(env, path); + 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) { android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader( env, classLoader); @@ -44,6 +52,7 @@ void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) { const JNINativeMethod g_methods[] = { { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, + { "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/android_text_LineBreaker.cpp b/core/jni/android_text_LineBreaker.cpp index dac108ef5497..543910727ffd 100644 --- a/core/jni/android_text_LineBreaker.cpp +++ b/core/jni/android_text_LineBreaker.cpp @@ -41,17 +41,6 @@ namespace android { -struct JLineBreaksID { - jfieldID breaks; - jfieldID widths; - jfieldID ascents; - jfieldID descents; - jfieldID flags; -}; - -static jclass gLineBreaks_class; -static JLineBreaksID gLineBreaks_fieldID; - static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) { if (javaArray == nullptr) { return std::vector<float>(); @@ -85,34 +74,7 @@ static jlong nGetReleaseFunc() { return reinterpret_cast<jlong>(nFinish); } -static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, - jfloatArray recycleWidths, jfloatArray recycleAscents, - jfloatArray recycleDescents, jintArray recycleFlags, - jint recycleLength, const minikin::LineBreakResult& result) { - const size_t nBreaks = result.breakPoints.size(); - if ((size_t)recycleLength < nBreaks) { - // have to reallocate buffers - recycleBreaks = env->NewIntArray(nBreaks); - recycleWidths = env->NewFloatArray(nBreaks); - recycleAscents = env->NewFloatArray(nBreaks); - recycleDescents = env->NewFloatArray(nBreaks); - recycleFlags = env->NewIntArray(nBreaks); - - env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks); - env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths); - env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents); - env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents); - env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags); - } - // copy data - env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data()); - env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data()); - env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data()); - env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data()); - env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data()); -} - -static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, +static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, // Inputs jcharArray javaText, jlong measuredTextPtr, @@ -122,18 +84,7 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, jfloat restWidth, jintArray variableTabStops, jint defaultTabStop, - jint indentsOffset, - - // Outputs - jobject recycle, - jint recycleLength, - jintArray recycleBreaks, - jfloatArray recycleWidths, - jfloatArray recycleAscents, - jfloatArray recycleDescents, - jintArray recycleFlags, - jfloatArray charWidths) { - + jint indentsOffset) { minikin::android::StaticLayoutNative* builder = toNative(nativePtr); ScopedCharArrayRO text(env, javaText); @@ -141,14 +92,44 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, minikin::U16StringPiece u16Text(text.get(), length); minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr); - minikin::LineBreakResult result = builder->computeBreaks( - u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, - tabStops.get(), tabStops.size(), defaultTabStop); - recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents, - recycleFlags, recycleLength, result); + std::unique_ptr<minikin::LineBreakResult> result = + std::make_unique<minikin::LineBreakResult>(builder->computeBreaks( + u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, + tabStops.get(), tabStops.size(), defaultTabStop)); + return reinterpret_cast<jlong>(result.release()); +} + +static jint nGetLineCount(jlong ptr) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints.size(); +} + +static jint nGetLineBreakOffset(jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints[i]; +} + +static jfloat nGetLineWidth(jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->widths[i]; +} - return static_cast<jint>(result.breakPoints.size()); +static jfloat nGetLineAscent(jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->ascents[i]; +} + +static jfloat nGetLineDescent(jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->descents[i]; +} + +static jint nGetLineFlag(jlong ptr, jint i) { + return reinterpret_cast<minikin::LineBreakResult*>(ptr)->flags[i]; +} + +static void nReleaseResult(jlong ptr) { + delete reinterpret_cast<minikin::LineBreakResult*>(ptr); +} + +static jlong nGetReleaseResultFunc() { + return reinterpret_cast<jlong>(nReleaseResult); } static const JNINativeMethod gMethods[] = { @@ -166,8 +147,6 @@ static const JNINativeMethod gMethods[] = { // Regular JNI {"nComputeLineBreaks", "(" "J" // nativePtr - - // Inputs "[C" // text "J" // MeasuredParagraph ptr. "I" // length @@ -177,31 +156,20 @@ static const JNINativeMethod gMethods[] = { "[I" // variableTabStops "I" // defaultTabStop "I" // indentsOffset - - // Outputs - "Landroid/text/NativeLineBreaker$LineBreaks;" // recycle - "I" // recycleLength - "[I" // recycleBreaks - "[F" // recycleWidths - "[F" // recycleAscents - "[F" // recycleDescents - "[I" // recycleFlags - ")I", (void*) nComputeLineBreaks} + ")J", (void*) nComputeLineBreaks}, + + // Result accessors, CriticalNatives + {"nGetLineCount", "(J)I", (void*)nGetLineCount}, + {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset}, + {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth}, + {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent}, + {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent}, + {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag}, + {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc}, }; -int register_android_text_LineBreaker(JNIEnv* env) -{ - gLineBreaks_class = MakeGlobalRefOrDie(env, - FindClassOrDie(env, "android/text/NativeLineBreaker$LineBreaks")); - - gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I"); - gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F"); - gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F"); - gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F"); - gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I"); - - return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", - gMethods, NELEM(gMethods)); +int register_android_text_LineBreaker(JNIEnv* env) { + return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods)); } } diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 3c59bd1e3856..7a5b60493dcd 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -1064,6 +1064,12 @@ static void android_view_ThreadedRenderer_allocateBuffers(JNIEnv* env, jobject c proxy->allocateBuffers(surface); } +static void android_view_ThreadedRenderer_setForceDark(JNIEnv* env, jobject clazz, + jlong proxyPtr, jboolean enable) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + proxy->setForceDark(enable); +} + // ---------------------------------------------------------------------------- // FrameMetricsObserver // ---------------------------------------------------------------------------- @@ -1177,6 +1183,7 @@ static const JNINativeMethod gMethods[] = { { "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess }, { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority }, { "nAllocateBuffers", "(JLandroid/view/Surface;)V", (void*)android_view_ThreadedRenderer_allocateBuffers }, + { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark }, }; static JavaVM* mJvm = nullptr; diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp index c15b7ee4fe04..109e65c4a1d0 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 statsLinesToNetworkStats(JNIEnv* env, jclass clazz, jobject stats, 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 @@ static int readNetworkStatsDetail(JNIEnv* env, jclass clazz, jobject stats, jstr 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 364393e1c649..1f958628374d 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -382,11 +382,10 @@ static int UnmountTree(const char* path) { 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 @@ static bool createPkgSandbox(uid_t uid, const char* package_name, std::string& p *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 @@ static bool createPkgSandbox(uid_t uid, const char* package_name, std::string& p 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 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, 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 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi 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 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( 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 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( 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 @@ static const JNINativeMethod gMethods[] = { { "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 f9f725a130ed..14ed9e6eeadb 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -397,6 +397,9 @@ message GlobalSettingsProto { // 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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ade0b111111d..9acb08b25db2 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" @@ -4463,14 +4462,6 @@ android:permission="android.permission.LOCATION_HARDWARE" android:exported="false" /> - <service android:name="com.android.internal.backup.LocalTransportService" - android:permission="android.permission.CONFIRM_FULL_BACKUP" - android:exported="false"> - <intent-filter> - <action android:name="android.backup.TRANSPORT_HOST" /> - </intent-filter> - </service> - <service android:name="com.android.server.MountServiceIdler" android:exported="true" android:permission="android.permission.BIND_JOB_SERVICE" > diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 6ae25417e386..bebd4896e89b 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 41616df4209d..912db2001851 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/config.xml b/core/res/res/values/config.xml index 9aebf6c4597f..0daf5a76562d 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/public.xml b/core/res/res/values/public.xml index cc99a4e4e640..cdaff18cf38a 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2912,6 +2912,7 @@ <!-- @hide For use by platform and tools only. Developers should not specify this value. --> <public name="usesNonSdkApi" /> <public name="minimumUiTimeout" /> + <public name="isLightTheme" /> </public-group> <public-group type="drawable" first-id="0x010800b4"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9f2256a6461a..72ae0d61654a 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/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index fee470dce24d..8f0e76bdc0c9 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -447,6 +447,7 @@ public class SettingsBackupTest { Settings.Global.ENABLE_GPU_DEBUG_LAYERS, Settings.Global.GPU_DEBUG_APP, Settings.Global.GPU_DEBUG_LAYERS, + Settings.Global.ANGLE_ENABLED_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS, diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java index 890929374159..b0d29bd1d8f0 100644 --- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java +++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java @@ -73,7 +73,7 @@ public class SettingsValidatorsTest { @Test public void testComponentNameValidator() { assertTrue(SettingsValidators.COMPONENT_NAME_VALIDATOR.validate( - "android/com.android.internal.backup.LocalTransport")); + "com.android.localtransport/.LocalTransport")); assertFalse(SettingsValidators.COMPONENT_NAME_VALIDATOR.validate("rectangle")); } @@ -90,7 +90,7 @@ public class SettingsValidatorsTest { @Test public void testNullableComponentNameValidator_onValidComponentName_returnsTrue() { assertTrue(SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR.validate( - "android/com.android.internal.backup.LocalTransport")); + "com.android.localtransport/.LocalTransport")); } @Test @@ -185,7 +185,7 @@ public class SettingsValidatorsTest { @Test public void testComponentNameListValidator() { Validator v = new SettingsValidators.ComponentNameListValidator(","); - assertTrue(v.validate("android/com.android.internal.backup.LocalTransport," + assertTrue(v.validate("com.android.localtransport/.LocalTransport," + "com.google.android.gms/.backup.migrate.service.D2dTransport")); assertFalse(v.validate("com.google.5android,android")); } @@ -200,7 +200,7 @@ public class SettingsValidatorsTest { @Test public void testPackageNameListValidator() { Validator v = new SettingsValidators.PackageNameListValidator(","); - assertTrue(v.validate("com.android.internal.backup.LocalTransport,com.google.android.gms")); + assertTrue(v.validate("com.android.localtransport.LocalTransport,com.google.android.gms")); assertFalse(v.validate("5com.android.internal.backup.LocalTransport,android")); } diff --git a/data/etc/framework-sysconfig.xml b/data/etc/framework-sysconfig.xml index ae6a7f6d6808..b0d2de17527d 100644 --- a/data/etc/framework-sysconfig.xml +++ b/data/etc/framework-sysconfig.xml @@ -28,7 +28,7 @@ <!-- Whitelist of what components are permitted as backup data transports. The 'service' attribute here is a flattened ComponentName string. --> <backup-transport-whitelisted-service - service="android/com.android.internal.backup.LocalTransportService" /> + service="com.android.localtransport/.LocalTransportService" /> <!-- Whitelist of bundled applications which all handle URLs to their websites by default --> <app-link package="com.android.carrierdefaultapp" /> diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 492c236c014e..e6ac06011e23 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -34,7 +34,6 @@ import android.os.Build; import android.provider.FontRequest; import android.provider.FontsContract; import android.text.FontConfig; -import android.util.ArrayMap; import android.util.Base64; import android.util.LongSparseArray; import android.util.LruCache; @@ -1112,13 +1111,6 @@ public class Typeface { } } - // Following methods are left for layoutlib - // TODO: Remove once layoutlib stop calling buildSystemFallback - /** @hide */ - public static void buildSystemFallback(String xmlPath, String fontDir, - ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) { - } - static { final HashMap<String, Typeface> systemFontMap = new HashMap<>(); initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 11dad2e0d731..494e5135d5bb 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -229,6 +229,7 @@ cc_defaults { "ResourceCache.cpp", "SkiaCanvas.cpp", "Snapshot.cpp", + "TreeInfo.cpp", "VectorDrawable.cpp", "protos/graphicsstats.proto", ], diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 21fbbdca7ad0..0b9d82b105a3 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -61,18 +61,6 @@ DisplayInfo QueryDisplayInfo() { return displayInfo; } -void QueryCompositionPreference(ui::Dataspace* dataSpace, - ui::PixelFormat* pixelFormat) { - if (Properties::isolatedProcess) { - *dataSpace = ui::Dataspace::V0_SRGB; - *pixelFormat = ui::PixelFormat::RGBA_8888; - } - - status_t status = - SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat); - LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status); -} - DeviceInfo::DeviceInfo() { #if HWUI_NULL_GPU mMaxTextureSize = NULL_GPU_MAX_TEXTURE_SIZE; @@ -80,7 +68,6 @@ DeviceInfo::DeviceInfo() { mMaxTextureSize = -1; #endif mDisplayInfo = QueryDisplayInfo(); - QueryCompositionPreference(&mTargetDataSpace, &mTargetPixelFormat); } int DeviceInfo::maxTextureSize() const { diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 1d7477416077..595621573e6e 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -17,7 +17,6 @@ #define DEVICEINFO_H #include <ui/DisplayInfo.h> -#include <ui/GraphicTypes.h> #include "utils/Macros.h" @@ -37,9 +36,6 @@ public: // this value is only valid after the GPU has been initialized and there is a valid graphics // context or if you are using the HWUI_NULL_GPU int maxTextureSize() const; - - ui::Dataspace getTargetDataSpace() const { return mTargetDataSpace; } - ui::PixelFormat getTargetPixelFormat() const { return mTargetPixelFormat; } const DisplayInfo& displayInfo() const { return mDisplayInfo; } private: @@ -50,10 +46,6 @@ private: int mMaxTextureSize; DisplayInfo mDisplayInfo; - - // TODO(lpy) Replace below with android_ prefix types. - ui::Dataspace mTargetDataSpace; - ui::PixelFormat mTargetPixelFormat; }; } /* namespace uirenderer */ diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 17bec1934490..a699e2f7195b 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -61,6 +61,7 @@ bool Properties::filterOutTestOverhead = false; bool Properties::disableVsync = false; bool Properties::skpCaptureEnabled = false; bool Properties::forceDarkMode = false; +bool Properties::enableForceDarkSupport = false; bool Properties::enableRTAnimations = true; bool Properties::runningInEmulator = false; @@ -149,6 +150,9 @@ bool Properties::load() { forceDarkMode = property_get_bool(PROPERTY_FORCE_DARK, false); + // TODO: make this on by default + enableForceDarkSupport = property_get_bool(PROPERTY_ENABLE_FORCE_DARK, false); + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) || (prevDebugStencilClip != debugStencilClip); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index ea017a72cd74..542bc71f7c72 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -192,6 +192,8 @@ enum DebugLevel { #define PROPERTY_FORCE_DARK "debug.hwui.force_dark" +#define PROPERTY_ENABLE_FORCE_DARK "debug.hwui.force_dark_enabled" + /////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////// @@ -266,6 +268,7 @@ public: static bool skpCaptureEnabled; static bool forceDarkMode; + static bool enableForceDarkSupport; // For experimentation b/68769804 ANDROID_API static bool enableRTAnimations; diff --git a/libs/hwui/TreeInfo.cpp b/libs/hwui/TreeInfo.cpp new file mode 100644 index 000000000000..808a12a311e2 --- /dev/null +++ b/libs/hwui/TreeInfo.cpp @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#include "TreeInfo.h" + +#include "renderthread/CanvasContext.h" + +namespace android::uirenderer { + +TreeInfo::TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext) + : mode(mode) + , prepareTextures(mode == MODE_FULL) + , canvasContext(canvasContext) + , disableForceDark(canvasContext.useForceDark() ? 0 : 1) {} + +} // namespace android::uirenderer diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index caa5762d174a..a0d960527ca6 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -16,8 +16,8 @@ #pragma once -#include "utils/Macros.h" #include "Properties.h" +#include "utils/Macros.h" #include <utils/Timers.h> @@ -40,7 +40,7 @@ public: virtual void onError(const std::string& message) = 0; protected: - virtual ~ErrorHandler() {} + virtual ~ErrorHandler() = default; }; class TreeObserver { @@ -52,7 +52,7 @@ public: virtual void onMaybeRemovedFromTree(RenderNode* node) = 0; protected: - virtual ~TreeObserver() {} + virtual ~TreeObserver() = default; }; // This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN @@ -71,8 +71,7 @@ public: MODE_RT_ONLY, }; - TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext) - : mode(mode), prepareTextures(mode == MODE_FULL), canvasContext(canvasContext) {} + TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContext); TraversalMode mode; // TODO: Remove this? Currently this is used to signal to stop preparing @@ -94,7 +93,7 @@ public: bool updateWindowPositions = false; - int disableForceDark = Properties::forceDarkMode ? 0 : 1; + int disableForceDark; struct Out { bool hasFunctors = false; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index e8bf4922cd46..d401b385075e 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -167,6 +167,12 @@ bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, mEglSurface = mEglManager.createSurface(surface, colorMode); } + if (colorMode == ColorMode::SRGB) { + mSurfaceColorType = SkColorType::kN32_SkColorType; + } else if (colorMode == ColorMode::WideColorGamut) { + mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType; + } + if (mEglSurface != EGL_NO_SURFACE) { const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer); mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); @@ -184,14 +190,6 @@ bool SkiaOpenGLPipeline::isContextReady() { return CC_LIKELY(mEglManager.hasEglContext()); } -SkColorType SkiaOpenGLPipeline::getSurfaceColorType() const { - return mEglManager.getSurfaceColorType(); -} - -sk_sp<SkColorSpace> SkiaOpenGLPipeline::getSurfaceColorSpace() { - return mEglManager.getSurfaceColorSpace(); -} - void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; if (thread.eglManager().hasEglContext()) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 086a76088a75..4ab3541d447b 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -47,8 +47,6 @@ public: void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; - SkColorType getSurfaceColorType() const override; - sk_sp<SkColorSpace> getSurfaceColorSpace() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index ee9158c5ffc1..42a411a6808c 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -48,6 +48,9 @@ public: bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; + SkColorType getSurfaceColorType() const { return mSurfaceColorType; } + sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; } + void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect& contentDrawBounds, sk_sp<SkSurface> surface); @@ -106,6 +109,8 @@ protected: void dumpResourceCacheUsage() const; renderthread::RenderThread& mRenderThread; + SkColorType mSurfaceColorType; + sk_sp<SkColorSpace> mSurfaceColorSpace; private: void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 4ef30fc6bebc..a2d811993f2f 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -137,14 +137,6 @@ bool SkiaVulkanPipeline::isContextReady() { return CC_LIKELY(mVkManager.hasVkContext()); } -SkColorType SkiaVulkanPipeline::getSurfaceColorType() const { - return mVkManager.getSurfaceColorType(); -} - -sk_sp<SkColorSpace> SkiaVulkanPipeline::getSurfaceColorSpace() { - return mVkManager.getSurfaceColorSpace(); -} - void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { VkFunctorDrawable::vkInvokeFunctor(functor); } diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 6e723a8373e1..14c0d69dba33 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -43,8 +43,6 @@ public: void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; - SkColorType getSurfaceColorType() const override; - sk_sp<SkColorSpace> getSurfaceColorSpace() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index c8c394a72541..92a749f3da33 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -144,8 +144,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { mNativeSurface = std::move(surface); - // TODO(b/111436479) Introduce a way for app to specify DisplayColorGamut mode. - ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Legacy; + ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode); mFrameNumber = -1; diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 2315cb9c73f9..2307ee4801d3 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -182,6 +182,23 @@ public: mFrameCompleteCallbacks.push_back(std::move(func)); } + void setForceDark(bool enable) { + mUseForceDark = enable; + } + + bool useForceDark() { + // The force-dark override has the highest priority, followed by the disable setting + // for the feature as a whole, followed last by whether or not this context has had + // force dark set (typically automatically done via UIMode) + if (Properties::forceDarkMode) { + return true; + } + if (!Properties::enableForceDarkSupport) { + return false; + } + return mUseForceDark; + } + private: CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline); @@ -228,6 +245,7 @@ private: bool mOpaque; bool mWideColorGamut = false; + bool mUseForceDark = false; LightInfo mLightInfo; LightGeometry mLightGeometry = {{0, 0, 0}, 0}; diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 0cb23e532064..d4ffddde8def 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -126,17 +126,6 @@ void EglManager::initialize() { createContext(); createPBufferSurface(); makeCurrent(mPBufferSurface, nullptr, /* force */ true); - - mSurfaceColorGamut = DataSpaceToColorGamut( - static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace())); - - LOG_ALWAYS_FATAL_IF(mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut && - !EglExtensions.displayP3, "EGL doesn't support Display P3."); - - mSurfaceColorType = PixelFormatToColorType( - static_cast<android_pixel_format>(DeviceInfo::get()->getTargetPixelFormat())); - mSurfaceColorSpace = DataSpaceToColorSpace( - static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace())); } void EglManager::initExtensions() { @@ -309,21 +298,13 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, ColorMode color if (wideColorGamut) { attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT; } else { - if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) { - attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT; - } else { - attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR; - } + attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR; } #else if (wideColorGamut) { attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT; } else { - if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) { - attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT; - } else { - attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; - } + attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; } #endif } diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index e97228cd0a39..55c81d42d8a0 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -78,9 +78,6 @@ public: // Depending on installed extensions, the result is either Android native fence or EGL fence. status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence); - SkColorType getSurfaceColorType() const { return mSurfaceColorType; } - sk_sp<SkColorSpace> getSurfaceColorSpace() { return mSurfaceColorSpace; } - private: void initExtensions(); @@ -95,9 +92,6 @@ private: EGLContext mEglContext; EGLSurface mPBufferSurface; EGLSurface mCurrentSurface; - SkColorSpace::Gamut mSurfaceColorGamut; - SkColorType mSurfaceColorType; - sk_sp<SkColorSpace> mSurfaceColorSpace; enum class SwapBehavior { Discard, diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 0297c9c141ff..4972554c65cc 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -43,15 +43,8 @@ namespace renderthread { enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded }; enum class ColorMode { - // Legacy means HWUI will produce buffer with whatever platform prefers - // HWUI to produce, however, HWUI doesn't accurately convert color from - // source color space to destination color space, instead HWUI will take - // the pixel value directly and interpret it destination color space. - Legacy, - // DisplayColorGamut means HWUI will produce buffer with whatever platform - // prefers HWUI to produce and accurately convert color from source color - // space to destination color space. - DisplayColorGamut, + // SRGB means HWUI will produce buffer in SRGB color space. + SRGB, // WideColorGamut means HWUI would support rendering scRGB non-linear into // a signed buffer with enough range to support the wide color gamut of the // display. diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 6106e24c093b..54219b5a1489 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -298,6 +298,12 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observerPtr) }); } +void RenderProxy::setForceDark(bool enable) { + mRenderThread.queue().post([this, enable]() { + mContext->setForceDark(enable); + }); +} + int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, SkBitmap* bitmap) { auto& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index d22f56ef38fd..d29fcc49d7a6 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -119,7 +119,7 @@ public: ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer); ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer); - ANDROID_API long getDroppedFrameReportCount(); + ANDROID_API void setForceDark(bool enable); ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom, SkBitmap* bitmap); diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index e54eb6a0c5f1..7c59b6d340d2 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -120,10 +120,6 @@ public: // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. status_t createReleaseFence(sp<Fence>& nativeFence); - // TODO(b/115636873): Handle composition preference. - SkColorType getSurfaceColorType() const { return SkColorType::kN32_SkColorType; } - sk_sp<SkColorSpace> getSurfaceColorSpace() { return SkColorSpace::MakeSRGB(); } - private: friend class RenderThread; diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 680fcb3a732b..cdf31da37074 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -386,7 +386,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); EXPECT_FALSE(pipeline->isSurfaceReady()); - EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::Legacy)); + EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB)); EXPECT_TRUE(pipeline->isSurfaceReady()); renderThread.destroyGlContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index 9f71e91629fb..3fb6a31a7d97 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -57,21 +57,6 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) { return false; } -SkColorType PixelFormatToColorType(android_pixel_format pixelFormat) { - switch (pixelFormat) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_BGRA_8888: - return SkColorType::kN32_SkColorType; - case HAL_PIXEL_FORMAT_RGBA_FP16: - return SkColorType::kRGBA_F16_SkColorType; - case HAL_PIXEL_FORMAT_RGBA_1010102: - return SkColorType::kRGBA_1010102_SkColorType; - default: - ALOGW("Unsupported pixel format: %d, return kN32 by default", pixelFormat); - return SkColorType::kN32_SkColorType; - } -} - android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) { switch (colorType) { case kRGBA_8888_SkColorType: @@ -92,30 +77,6 @@ android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType) { } } -SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace) { - switch (dataSpace & HAL_DATASPACE_STANDARD_MASK) { - case HAL_DATASPACE_STANDARD_BT709: - return SkColorSpace::kSRGB_Gamut; - case HAL_DATASPACE_STANDARD_BT2020: - return SkColorSpace::kRec2020_Gamut; - case HAL_DATASPACE_STANDARD_DCI_P3: - return SkColorSpace::kDCIP3_D65_Gamut; - case HAL_DATASPACE_STANDARD_ADOBE_RGB: - return SkColorSpace::kAdobeRGB_Gamut; - case HAL_DATASPACE_STANDARD_UNSPECIFIED: - case HAL_DATASPACE_STANDARD_BT601_625: - case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED: - case HAL_DATASPACE_STANDARD_BT601_525: - case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED: - case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE: - case HAL_DATASPACE_STANDARD_BT470M: - case HAL_DATASPACE_STANDARD_FILM: - default: - ALOGW("Unsupported Gamut: %d, return SRGB gamut by default", dataSpace); - return SkColorSpace::kSRGB_Gamut; - } -} - sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { SkColorSpace::Gamut gamut; diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index e935a0d5ec8b..4daccda78e23 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -115,12 +115,8 @@ static constexpr float EOCF(float srgb) { // returns true for sRGB, gamma 2.2 and Display P3 for instance bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace); -SkColorType PixelFormatToColorType(android_pixel_format pixelFormat); - android::PixelFormat ColorTypeToPixelFormat(SkColorType colorType); -SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace); - sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); struct Lab { diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index ed4da22f69e7..340f27950638 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -336,7 +336,7 @@ import java.util.Vector; * * <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 7b7b800155cd..84d246f50ee3 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -271,7 +271,10 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) { @Override void process() { - // TODO: switch to next data source and play + if (getState() == PLAYER_STATE_PLAYING) { + pause(); + } + playNextDataSource(); } }); } diff --git a/packages/LocalTransport/Android.mk b/packages/LocalTransport/Android.mk new file mode 100644 index 000000000000..3484b0f7a537 --- /dev/null +++ b/packages/LocalTransport/Android.mk @@ -0,0 +1,35 @@ +# +# 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. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PROGUARD_FLAG_FILES := proguard.flags + +LOCAL_PACKAGE_NAME := LocalTransport +LOCAL_PRIVATE_PLATFORM_APIS := true +LOCAL_CERTIFICATE := platform +LOCAL_PRIVILEGED_MODULE := true + +include $(BUILD_PACKAGE) + +######################## +include $(call all-makefiles-under,$(LOCAL_PATH)) + diff --git a/packages/LocalTransport/AndroidManifest.xml b/packages/LocalTransport/AndroidManifest.xml new file mode 100644 index 000000000000..196be1e998f3 --- /dev/null +++ b/packages/LocalTransport/AndroidManifest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (c) 2018 Google Inc. + * + * 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.localtransport" + android:sharedUserId="android.uid.system" > + + + <application android:allowBackup="false" > + <!-- This service does not need to be exported because it shares uid with the system server + which is the only client. --> + <service android:name=".LocalTransportService" + android:permission="android.permission.CONFIRM_FULL_BACKUP" + android:exported="false"> + <intent-filter> + <action android:name="android.backup.TRANSPORT_HOST" /> + </intent-filter> + </service> + + </application> +</manifest> diff --git a/packages/LocalTransport/proguard.flags b/packages/LocalTransport/proguard.flags new file mode 100644 index 000000000000..c1f51b892d40 --- /dev/null +++ b/packages/LocalTransport/proguard.flags @@ -0,0 +1,5 @@ +-keep class com.android.localTransport.LocalTransport +-keep class com.android.localTransport.LocalTransportParameters +-keep class com.android.localTransport.LocalTransportService + + diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java index d0f02725b1a0..0bf8bc1051c2 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.backup; +package com.android.localtransport; import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; @@ -26,7 +26,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; -import android.os.Environment; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.Os; @@ -56,7 +55,7 @@ public class LocalTransport extends BackupTransport { private static final boolean DEBUG = false; private static final String TRANSPORT_DIR_NAME - = "com.android.internal.backup.LocalTransport"; + = "com.android.localtransport.LocalTransport"; private static final String TRANSPORT_DESTINATION_STRING = "Backing up to debug-only private cache"; @@ -75,10 +74,10 @@ public class LocalTransport extends BackupTransport { private static final long KEY_VALUE_BACKUP_SIZE_QUOTA = 5 * 1024 * 1024; private Context mContext; - private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); - private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN)); - private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR); - private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR); + private File mDataDir; + private File mCurrentSetDir; + private File mCurrentSetIncrementalDir; + private File mCurrentSetFullDir; private PackageInfo[] mRestorePackages = null; private int mRestorePackage = -1; // Index into mRestorePackages @@ -101,6 +100,11 @@ public class LocalTransport extends BackupTransport { private final LocalTransportParameters mParameters; private void makeDataDirs() { + mDataDir = mContext.getFilesDir(); + mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN)); + mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR); + mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR); + mCurrentSetDir.mkdirs(); mCurrentSetFullDir.mkdir(); mCurrentSetIncrementalDir.mkdir(); diff --git a/core/java/com/android/internal/backup/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java index 2427d39fd65e..784be224f367 100644 --- a/core/java/com/android/internal/backup/LocalTransportParameters.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.internal.backup; +package com.android.localtransport; import android.util.KeyValueSettingObserver; import android.content.ContentResolver; diff --git a/core/java/com/android/internal/backup/LocalTransportService.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportService.java index 69c48e2a48cf..ac4f418b68f6 100644 --- a/core/java/com/android/internal/backup/LocalTransportService.java +++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.backup; +package com.android.localtransport; import android.app.Service; import android.content.Intent; diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index ee4c95445bff..89438e555b0d 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -15,6 +15,7 @@ android_library { "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 a034d297bac8..2f576e62202e 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 000000000000..7541ca456138 --- /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 000000000000..b86544ec68c1 --- /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 000000000000..7e65848de189 --- /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 000000000000..0b12810ffc38 --- /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-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 3859092ebfa8..bd9a6ec335b7 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -446,7 +446,6 @@ <string name="alarm_template_far" msgid="3779172822607461675">"अलार्म <xliff:g id="WHEN">%1$s</xliff:g> को बजेगा"</string> <string name="zen_mode_duration_settings_title" msgid="229547412251222757">"अवधि"</string> <string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"हर बार पूछें"</string> - <!-- no translation found for zen_mode_forever (2704305038191592967) --> - <skip /> + <string name="zen_mode_forever" msgid="2704305038191592967">"जब तक आप इसे बंद नहीं करते"</string> <string name="time_unit_just_now" msgid="6363336622778342422">"अभी-अभी"</string> </resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 332ced66ef77..508adbd2a121 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 cb1989157db8..14f96269c54e 100644 --- a/packages/SettingsLib/search/Android.mk +++ b/packages/SettingsLib/search/Android.mk @@ -5,6 +5,9 @@ LOCAL_MODULE = SettingsLib-search 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 0c29f431ef3f..9653972414ee 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.BluetoothMapClient; 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 @@ public class LocalBluetoothProfileManager { 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 @@ public class LocalBluetoothProfileManager { 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 @@ public class LocalBluetoothProfileManager { 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 9a6f104fadd5..b4acc4810faf 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 @@ import java.util.List; */ 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 @@ final class SapProfile implements LocalBluetoothProfile { 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 @@ final class SapProfile implements LocalBluetoothProfile { } 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 @@ final class SapProfile implements LocalBluetoothProfile { 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 @@ final class SapProfile implements LocalBluetoothProfile { } 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 @@ final class SapProfile implements LocalBluetoothProfile { } 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 @@ final class SapProfile implements LocalBluetoothProfile { } 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 42306f6d46d0..9db4a35c1d78 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 @@ import java.util.zip.GZIPInputStream; * 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 @@ class LicenseHtmlGeneratorFromXml { 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 @@ class LicenseHtmlGeneratorFromXml { try { writer = new PrintWriter(outputFile); - generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer); + generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer, + noticeHeader); writer.flush(); writer.close(); @@ -239,13 +241,18 @@ class LicenseHtmlGeneratorFromXml { @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 393006940740..78e807cf1a1c 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 @@ public class LicenseHtmlLoader extends AsyncLoader<File> { 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 360c19c2b795..ca6248505dc0 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 @@ package com.android.settingslib.license; 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 @@ public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> { File cachedHtmlFile = getCachedHtmlFile(mContext); if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile) - || generateHtmlFile(xmlFiles, cachedHtmlFile)) { + || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) { return cachedHtmlFile; } @@ -101,7 +102,8 @@ public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> { 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 74bd97f40ff7..e9c523881373 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 @@ import com.android.settingslib.AppItem; /** * 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 000000000000..9b3ff8b2e165 --- /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 000000000000..7ae3398d42ea --- /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 2d8c0de42ba4..26c65a2c4a48 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 000000000000..9d13717bbbcc --- /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 000000000000..95efb4cea491 --- /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 80e13563d74b..cc936d2485c5 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.net.TrafficStats; 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 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat 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 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat } @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 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat 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 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat } 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 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat 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 000000000000..9bb53ee6a343 --- /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 96b2a1433f53..b00476b24921 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 @@ public class LicenseHtmlGeneratorFromXmlTest { + "<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 @@ public class LicenseHtmlGeneratorFromXmlTest { + "</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 @@ public class LicenseHtmlGeneratorFromXmlTest { + "</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 @@ public class LicenseHtmlGeneratorFromXmlTest { 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 12a4e699fb76..c32cc99daf96 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 @@ public class LicenseHtmlLoaderCompatTest { } @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 000000000000..3dc110d30e1e --- /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 000000000000..53fe45197236 --- /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 4c4207b23cab..be7f1bbb280f 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 org.robolectric.util.ReflectionHelpers; import java.time.ZonedDateTime; import java.util.Iterator; +import java.util.List; @RunWith(SettingsLibRobolectricTestRunner.class) public class NetworkCycleDataLoaderTest { @@ -64,8 +62,11 @@ public class NetworkCycleDataLoaderTest { 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 @@ public class NetworkCycleDataLoaderTest { @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 @@ public class NetworkCycleDataLoaderTest { } @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 @@ public class NetworkCycleDataLoaderTest { 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 d8e73b7db322..000000000000 --- 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/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index c53417b4e0f5..de86789053e9 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -55,7 +55,7 @@ <bool name="def_networks_available_notification_on">true</bool> <bool name="def_backup_enabled">false</bool> - <string name="def_backup_transport" translatable="false">android/com.android.internal.backup.LocalTransport</string> + <string name="def_backup_transport" translatable="false">com.android.localtransport/.LocalTransport</string> <!-- Default value for whether or not to pulse the notification LED when there is a pending notification --> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index bd21b8353136..c09b76394340 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 @@ class SettingsProtoDumpUtil { 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/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 63978ba60171..3d193db392a4 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3285,8 +3285,8 @@ public class SettingsProvider extends ContentProvider { if (currentVersion == 133) { // Version 133: Add default end button behavior final SettingsState systemSettings = getSystemSettingsLocked(userId); - if (systemSettings.getSettingLocked(Settings.System.END_BUTTON_BEHAVIOR) == - null) { + if (systemSettings.getSettingLocked(Settings.System.END_BUTTON_BEHAVIOR) + .isNull()) { String defaultEndButtonBehavior = Integer.toString(getContext() .getResources().getInteger(R.integer.def_end_button_behavior)); systemSettings.insertSettingLocked(Settings.System.END_BUTTON_BEHAVIOR, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java index 36fb3a7b4307..7154f5396fbd 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java @@ -21,6 +21,8 @@ import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LE import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import android.app.ActivityOptions; +import android.content.Context; +import android.os.Handler; /** * Wrapper around internal ActivityOptions creation. @@ -43,4 +45,17 @@ public abstract class ActivityOptionsCompat { RemoteAnimationAdapterCompat remoteAnimationAdapter) { return ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter.getWrapped()); } + + public static ActivityOptions makeCustomAnimation(Context context, int enterResId, + int exitResId, final Runnable callback, final Handler callbackHandler) { + return ActivityOptions.makeCustomAnimation(context, enterResId, exitResId, callbackHandler, + new ActivityOptions.OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + if (callback != null) { + callbackHandler.post(callback); + } + } + }); + } } diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index a3b539588d9b..0215fda81485 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -61,13 +61,14 @@ public class SwipeHelper implements Gefingerpoken { public static final float SWIPED_FAR_ENOUGH_SIZE_FRACTION = 0.6f; static final float MAX_SCROLL_SIZE_FRACTION = 0.3f; + protected final Handler mHandler; + private float mMinSwipeProgress = 0f; private float mMaxSwipeProgress = 1f; private final FlingAnimationUtils mFlingAnimationUtils; private float mPagingTouchSlop; private final Callback mCallback; - private final Handler mHandler; private final int mSwipeDirection; private final VelocityTracker mVelocityTracker; private final FalsingManager mFalsingManager; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index e6026c14cd28..21b21d9f5527 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 class DozeLog { 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 @@ public class DozeLog { 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 @@ public class DozeLog { 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 f9dfb5d10403..701394763fbd 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 @@ public class DozeSensors { 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/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 31548b93ea60..cb91d7815be5 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 @@ public class DozeTriggers implements DozeMachine.Part { 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 @@ public class DozeTriggers implements DozeMachine.Part { 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 @@ public class DozeTriggers implements DozeMachine.Part { 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/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 72c2c0bec31f..9978ec364cdb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -87,7 +87,6 @@ import com.android.systemui.classifier.FalsingManager; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; -import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; @@ -109,6 +108,7 @@ import com.android.systemui.statusbar.notification.NotificationData; import com.android.systemui.statusbar.notification.row.NotificationGuts; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.NotificationShelf; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.StatusBarStateController; @@ -146,11 +146,9 @@ import java.util.function.BiConsumer; * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. */ public class NotificationStackScrollLayout extends ViewGroup - implements Callback, ExpandHelper.Callback, ScrollAdapter, - OnHeightChangedListener, OnGroupChangeListener, - OnMenuEventListener, VisibilityLocationProvider, - NotificationListContainer, ConfigurationListener, DragDownCallback, AnimationStateHandler, - Dumpable { + implements ExpandHelper.Callback, ScrollAdapter, OnHeightChangedListener, + OnGroupChangeListener, VisibilityLocationProvider, NotificationListContainer, + ConfigurationListener, DragDownCallback, AnimationStateHandler, Dumpable { public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "StackScroller"; @@ -164,7 +162,7 @@ public class NotificationStackScrollLayout extends ViewGroup private static final int INVALID_POINTER = -1; private ExpandHelper mExpandHelper; - private NotificationSwipeHelper mSwipeHelper; + private final NotificationSwipeHelper mSwipeHelper; private boolean mSwipingInProgress; private int mCurrentStackHeight = Integer.MAX_VALUE; private final Paint mBackgroundPaint = new Paint(); @@ -291,10 +289,6 @@ public class NotificationStackScrollLayout extends ViewGroup */ private int mMaxScrollAfterExpand; private ExpandableNotificationRow.LongPressListener mLongPressListener; - - private NotificationMenuRowPlugin mCurrMenuRow; - private View mTranslatingParentView; - private View mMenuExposedView; boolean mCheckForLeavebehind; /** @@ -466,6 +460,9 @@ public class NotificationStackScrollLayout extends ViewGroup private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN; private NotificationPanelView mNotificationPanel; + private final NotificationGutsManager + mNotificationGutsManager = Dependency.get(NotificationGutsManager.class); + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public NotificationStackScrollLayout(Context context) { this(context, null); @@ -495,7 +492,8 @@ public class NotificationStackScrollLayout extends ViewGroup minHeight, maxHeight); mExpandHelper.setEventSource(this); mExpandHelper.setScrollAdapter(this); - mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext()); + mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, new SwipeHelperCallback(), + getContext(), new NotificationMenuListener()); mStackScrollAlgorithm = createStackScrollAlgorithm(context); initView(context); mFalsingManager = FalsingManager.getInstance(context); @@ -639,41 +637,6 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void onMenuClicked(View view, int x, int y, MenuItem item) { - if (mLongPressListener == null) { - return; - } - if (view instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR, - row.getStatusBarNotification().getPackageName()); - } - mLongPressListener.onLongPress(view, x, y, item); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void onMenuReset(View row) { - if (mTranslatingParentView != null && row == mTranslatingParentView) { - mMenuExposedView = null; - mTranslatingParentView = null; - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void onMenuShown(View row) { - mMenuExposedView = mTranslatingParentView; - if (row instanceof ExpandableNotificationRow) { - MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR, - ((ExpandableNotificationRow) row).getStatusBarNotification() - .getPackageName()); - } - mSwipeHelper.onMenuShown(row); - } - - @Override @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void onUiModeChanged() { mBgColor = mContext.getColor(R.color.notification_shade_background_color); @@ -1295,111 +1258,6 @@ public class NotificationStackScrollLayout extends ViewGroup mQsContainer = qsContainer; } - /** - * Handles cleanup after the given {@code view} has been fully swiped out (including - * re-invoking dismiss logic in case the notification has not made its way out yet). - */ - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onChildDismissed(View view) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - if (!row.isDismissed()) { - handleChildViewDismissed(view); - } - ViewGroup transientContainer = row.getTransientContainer(); - if (transientContainer != null) { - transientContainer.removeTransientView(view); - } - } - - /** - * Starts up notification dismiss and tells the notification, if any, to remove itself from - * layout. - * - * @param view view (e.g. notification) to dismiss from the layout - */ - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void handleChildViewDismissed(View view) { - if (mDismissAllInProgress) { - return; - } - - boolean isBlockingHelperShown = false; - - setSwipingInProgress(false); - if (mDragAnimPendingChildren.contains(view)) { - // We start the swipe and finish it in the same frame; we don't want a drag animation. - mDragAnimPendingChildren.remove(view); - } - mAmbientState.onDragFinished(view); - updateContinuousShadowDrawing(); - - if (view instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - if (row.isHeadsUp()) { - mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey()); - } - isBlockingHelperShown = - row.performDismissWithBlockingHelper(false /* fromAccessibility */); - } - - if (!isBlockingHelperShown) { - mSwipedOutViews.add(view); - } - mFalsingManager.onNotificationDismissed(); - if (mFalsingManager.shouldEnforceBouncer()) { - mStatusBar.executeRunnableDismissingKeyguard( - null, - null /* cancelAction */, - false /* dismissShade */, - true /* afterKeyguardGone */, - false /* deferred */); - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void onChildSnappedBack(View animView, float targetLeft) { - mAmbientState.onDragFinished(animView); - updateContinuousShadowDrawing(); - if (!mDragAnimPendingChildren.contains(animView)) { - if (mAnimationsEnabled) { - mSnappedBackChildren.add(animView); - mNeedsAnimation = true; - } - requestChildrenUpdate(); - } else { - // We start the swipe and snap back in the same frame, we don't want any animation - mDragAnimPendingChildren.remove(animView); - } - if (mCurrMenuRow != null && targetLeft == 0) { - mCurrMenuRow.resetMenu(); - mCurrMenuRow = null; - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) { - // Returning true prevents alpha fading. - return !mFadeNotificationsOnDismiss; - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void onBeginDrag(View v) { - mFalsingManager.onNotificatonStartDismissing(); - setSwipingInProgress(true); - mAmbientState.onBeginDrag(v); - updateContinuousShadowDrawing(); - if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) { - mDragAnimPendingChildren.add(v); - mNeedsAnimation = true; - } - requestChildrenUpdate(); - } - @ShadeViewRefactor(RefactorComponent.ADAPTER) public static boolean isPinnedHeadsUp(View v) { if (v instanceof ExpandableNotificationRow) { @@ -1418,41 +1276,6 @@ public class NotificationStackScrollLayout extends ViewGroup return false; } - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public void onDragCancelled(View v) { - mFalsingManager.onNotificatonStopDismissing(); - setSwipingInProgress(false); - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public float getFalsingThresholdFactor() { - return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; - } - - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public View getChildAtPosition(MotionEvent ev) { - View child = getChildAtPosition(ev.getX(), ev.getY()); - if (child instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) child; - ExpandableNotificationRow parent = row.getNotificationParent(); - if (parent != null && parent.areChildrenExpanded() - && (parent.areGutsExposed() - || mMenuExposedView == parent - || (parent.getNotificationChildren().size() == 1 - && parent.isClearable()))) { - // In this case the group is expanded and showing the menu for the - // group, further interaction should apply to the group, not any - // child notifications so we use the parent of the child. We also do the same - // if we only have a single child. - child = parent; - } - } - return child; - } - @ShadeViewRefactor(RefactorComponent.INPUT) public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) { getLocationOnScreen(mTempInt2); @@ -1696,18 +1519,11 @@ public class NotificationStackScrollLayout extends ViewGroup return mScrollingEnabled; } - @Override @ShadeViewRefactor(RefactorComponent.ADAPTER) - public boolean canChildBeDismissed(View v) { + private boolean canChildBeDismissed(View v) { return StackScrollAlgorithm.canChildBeDismissed(v); } - @Override - @ShadeViewRefactor(RefactorComponent.INPUT) - public boolean isAntiFalsingNeeded() { - return onKeyguard(); - } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private boolean onKeyguard() { return mStatusBarState == StatusBarState.KEYGUARD; @@ -1787,8 +1603,8 @@ public class NotificationStackScrollLayout extends ViewGroup } // Check if we need to clear any snooze leavebehinds - NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts(); - if (guts != null && !isTouchInView(ev, guts) + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts) && guts.getGutsContent() instanceof NotificationSnooze) { NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent(); if ((ns.isExpanded() && isCancelOrUp) @@ -3013,11 +2829,11 @@ public class NotificationStackScrollLayout extends ViewGroup } // Check if we need to clear any snooze leavebehinds boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; - NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts(); - if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt - && !scrollWantsIt) { + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt && + !expandWantsIt && !scrollWantsIt) { mCheckForLeavebehind = false; - mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */, + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */); } @@ -3077,8 +2893,8 @@ public class NotificationStackScrollLayout extends ViewGroup @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @Override public void cleanUpViewState(View child) { - if (child == mTranslatingParentView) { - mTranslatingParentView = null; + if (child == mSwipeHelper.getTranslatingParentView()) { + mSwipeHelper.clearTranslatingParentView(); } mCurrentStackScrollState.removeViewStateForView(child); } @@ -3986,7 +3802,7 @@ public class NotificationStackScrollLayout extends ViewGroup @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void checkSnoozeLeavebehind() { if (mCheckForLeavebehind) { - mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */, + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */); mCheckForLeavebehind = false; @@ -4068,7 +3884,7 @@ public class NotificationStackScrollLayout extends ViewGroup } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - private void setIsExpanded(boolean isExpanded) { + public void setIsExpanded(boolean isExpanded) { boolean changed = isExpanded != mIsExpanded; mIsExpanded = isExpanded; mStackScrollAlgorithm.setIsExpanded(isExpanded); @@ -5242,8 +5058,8 @@ public class NotificationStackScrollLayout extends ViewGroup setFooterView(footerView); } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void inflateEmptyShadeView() { + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + private void inflateEmptyShadeView() { EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_no_notifications, this, false); view.setText(R.string.empty_shade_text); @@ -5274,8 +5090,8 @@ public class NotificationStackScrollLayout extends ViewGroup mScrimController.setNotificationCount(getNotGoneChildCount()); } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public void setNotificationPanel(NotificationPanelView notificationPanelView) { + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + public void setNotificationPanel(NotificationPanelView notificationPanelView) { mNotificationPanel = notificationPanelView; } @@ -5293,306 +5109,29 @@ public class NotificationStackScrollLayout extends ViewGroup @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public interface OnOverscrollTopChangedListener { - /** - * Notifies a listener that the overscroll has changed. - * - * @param amount the amount of overscroll, in pixels - * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an - * unrubberbanded motion to directly expand overscroll view (e.g - * expand - * QS) - */ - void onOverscrollTopChanged(float amount, boolean isRubberbanded); - - /** - * Notify a listener that the scroller wants to escape from the scrolling motion and - * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS) - * - * @param velocity The velocity that the Scroller had when over flinging - * @param open Should the fling open or close the overscroll view. - */ - void flingTopOverscroll(float velocity, boolean open); - } - - @ShadeViewRefactor(RefactorComponent.INPUT) - private class NotificationSwipeHelper extends SwipeHelper - implements NotificationSwipeActionHelper { - private static final long COVER_MENU_DELAY = 4000; - private Runnable mFalsingCheck; - private Handler mHandler; - - private static final long SWIPE_MENU_TIMING = 200; - - public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) { - super(swipeDirection, callback, context); - mHandler = new Handler(); - mFalsingCheck = new Runnable() { - @Override - public void run() { - resetExposedMenuView(true /* animate */, true /* force */); - } - }; - } - - @Override - public void onDownUpdate(View currView, MotionEvent ev) { - mTranslatingParentView = currView; - if (mCurrMenuRow != null) { - mCurrMenuRow.onTouchStart(); - } - mCurrMenuRow = null; - mHandler.removeCallbacks(mFalsingCheck); - - // Slide back any notifications that might be showing a menu - resetExposedMenuView(true /* animate */, false /* force */); - - if (currView instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) currView; - - if (row.getEntry().hasFinishedInitialization()) { - mCurrMenuRow = row.createMenu(); - mCurrMenuRow.setMenuClickListener(NotificationStackScrollLayout.this); - mCurrMenuRow.onTouchStart(); - } - } - } - - private boolean swipedEnoughToShowMenu(NotificationMenuRowPlugin menuRow) { - return !swipedFarEnough() && menuRow.isSwipedEnoughToShowMenu(); - } - - @Override - public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) { - mHandler.removeCallbacks(mFalsingCheck); - if (mCurrMenuRow != null) { - mCurrMenuRow.onTouchMove(delta); - } - } - - @Override - public boolean handleUpEvent(MotionEvent ev, View animView, float velocity, - float translation) { - if (mCurrMenuRow != null) { - mCurrMenuRow.onTouchEnd(); - handleMenuRowSwipe(ev, animView, velocity, mCurrMenuRow); - return true; - } - return false; - } - - @Override - public boolean swipedFarEnough(float translation, float viewSize) { - return swipedFarEnough(); - } - - private void handleMenuRowSwipe(MotionEvent ev, View animView, float velocity, - NotificationMenuRowPlugin menuRow) { - if (!menuRow.shouldShowMenu()) { - // If the menu should not be shown, then there is no need to check if the a swipe - // should result in a snapping to the menu. As a result, just check if the swipe - // was enough to dismiss the notification. - if (isDismissGesture(ev)) { - dismiss(animView, velocity); - } else { - snapBack(animView, velocity); - menuRow.onSnapClosed(); - } - return; - } - - if (menuRow.isSnappedAndOnSameSide()) { - // Menu was snapped to previously and we're on the same side - handleSwipeFromSnap(ev, animView, velocity, menuRow); - } else { - // Menu has not been snapped, or was snapped previously but is now on - // the opposite side. - handleSwipeFromNonSnap(ev, animView, velocity, menuRow); - } - } - - private void handleSwipeFromNonSnap(MotionEvent ev, View animView, float velocity, - NotificationMenuRowPlugin menuRow) { - boolean isDismissGesture = isDismissGesture(ev); - final boolean gestureTowardsMenu = menuRow.isTowardsMenu(velocity); - final boolean gestureFastEnough = - mSwipeHelper.getMinDismissVelocity() <= Math.abs(velocity); - - final double timeForGesture = ev.getEventTime() - ev.getDownTime(); - final boolean showMenuForSlowOnGoing = !menuRow.canBeDismissed() - && timeForGesture >= SWIPE_MENU_TIMING; - - if (!isFalseGesture(ev) - && (swipedEnoughToShowMenu(menuRow) - && (!gestureFastEnough || showMenuForSlowOnGoing)) - || (gestureTowardsMenu && !isDismissGesture)) { - // Menu has not been snapped to previously and this is menu revealing gesture - snapOpen(animView, menuRow.getMenuSnapTarget(), velocity); - menuRow.onSnapOpen(); - } else if (isDismissGesture(ev) && !gestureTowardsMenu) { - dismiss(animView, velocity); - menuRow.onDismiss(); - } else { - snapBack(animView, velocity); - menuRow.onSnapClosed(); - } - } - - private void handleSwipeFromSnap(MotionEvent ev, View animView, float velocity, - NotificationMenuRowPlugin menuRow) { - boolean isDismissGesture = isDismissGesture(ev); - - final boolean withinSnapMenuThreshold = - menuRow.isWithinSnapMenuThreshold(); - - if (withinSnapMenuThreshold && !isDismissGesture) { - // Haven't moved enough to unsnap from the menu - menuRow.onSnapOpen(); - snapOpen(animView, menuRow.getMenuSnapTarget(), velocity); - } else if (isDismissGesture && !menuRow.shouldSnapBack()) { - // Only dismiss if we're not moving towards the menu - dismiss(animView, velocity); - menuRow.onDismiss(); - } else { - snapBack(animView, velocity); - menuRow.onSnapClosed(); - } - } - - @Override - public void dismissChild(final View view, float velocity, - boolean useAccelerateInterpolator) { - super.dismissChild(view, velocity, useAccelerateInterpolator); - if (mIsExpanded) { - // We don't want to quick-dismiss when it's a heads up as this might lead to closing - // of the panel early. - handleChildViewDismissed(view); - } - mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */, - false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - handleMenuCoveredOrDismissed(); - } - - @Override - public void snapChild(final View animView, final float targetLeft, float velocity) { - super.snapChild(animView, targetLeft, velocity); - onDragCancelled(animView); - if (targetLeft == 0) { - handleMenuCoveredOrDismissed(); - } - } - - @Override - public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) { - mStatusBar.setNotificationSnoozed(sbn, snoozeOption); - } - - private void handleMenuCoveredOrDismissed() { - if (mMenuExposedView != null && mMenuExposedView == mTranslatingParentView) { - mMenuExposedView = null; - } - } - - @Override - public Animator getViewTranslationAnimator(View v, float target, - AnimatorUpdateListener listener) { - if (v instanceof ExpandableNotificationRow) { - return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener); - } else { - return super.getViewTranslationAnimator(v, target, listener); - } - } - - @Override - public void setTranslation(View v, float translate) { - ((ExpandableView) v).setTranslation(translate); - } - - @Override - public float getTranslation(View v) { - return ((ExpandableView) v).getTranslation(); - } - - @Override - public void dismiss(View animView, float velocity) { - dismissChild(animView, velocity, - !swipedFastEnough(0, 0) /* useAccelerateInterpolator */); - } - - @Override - public void snapOpen(View animView, int targetLeft, float velocity) { - snapChild(animView, targetLeft, velocity); - } - - private void snapBack(View animView, float velocity) { - snapChild(animView, 0, velocity); - } - - @Override - public boolean swipedFastEnough(float translation, float velocity) { - return swipedFastEnough(); - } - - @Override - public float getMinDismissVelocity() { - return getEscapeVelocity(); - } - - public void onMenuShown(View animView) { - onDragCancelled(animView); - - // If we're on the lockscreen we want to false this. - if (isAntiFalsingNeeded()) { - mHandler.removeCallbacks(mFalsingCheck); - mHandler.postDelayed(mFalsingCheck, COVER_MENU_DELAY); - } - } - - public void closeControlsIfOutsideTouch(MotionEvent ev) { - NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts(); - View view = null; - if (guts != null && !guts.getGutsContent().isLeavebehind()) { - // Only close visible guts if they're not a leavebehind. - view = guts; - } else if (mCurrMenuRow != null && mCurrMenuRow.isMenuVisible() - && mTranslatingParentView != null) { - // Checking menu - view = mTranslatingParentView; - } - if (view != null && !isTouchInView(ev, view)) { - // Touch was outside visible guts / menu notification, close what's visible - mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */, - false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - resetExposedMenuView(true /* animate */, true /* force */); - } - } + /** + * Notifies a listener that the overscroll has changed. + * + * @param amount the amount of overscroll, in pixels + * @param isRubberbanded if true, this is a rubberbanded overscroll; if false, this is an + * unrubberbanded motion to directly expand overscroll view (e.g + * expand + * QS) + */ + void onOverscrollTopChanged(float amount, boolean isRubberbanded); - public void resetExposedMenuView(boolean animate, boolean force) { - if (mMenuExposedView == null - || (!force && mMenuExposedView == mTranslatingParentView)) { - // If no menu is showing or it's showing for this view we do nothing. - return; - } - final View prevMenuExposedView = mMenuExposedView; - if (animate) { - Animator anim = getViewTranslationAnimator(prevMenuExposedView, - 0 /* leftTarget */, null /* updateListener */); - if (anim != null) { - anim.start(); - } - } else if (mMenuExposedView instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) mMenuExposedView; - if (!row.isRemoved()) { - row.resetTranslation(); - } - } - mMenuExposedView = null; - } - } + /** + * Notify a listener that the scroller wants to escape from the scrolling motion and + * start a fling animation to the expanded or collapsed overscroll view (e.g expand the QS) + * + * @param velocity The velocity that the Scroller had when over flinging + * @param open Should the fling open or close the overscroll view. + */ + void flingTopOverscroll(float velocity, boolean open); + } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public boolean hasActiveNotifications() { + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + public boolean hasActiveNotifications() { return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty(); } @@ -5656,8 +5195,8 @@ public class NotificationStackScrollLayout extends ViewGroup return mStatusBarState == StatusBarState.KEYGUARD; } - @ShadeViewRefactor(RefactorComponent.INPUT) - public void updateSpeedBumpIndex() { + @ShadeViewRefactor(RefactorComponent.INPUT) + public void updateSpeedBumpIndex() { int speedBumpIndex = 0; int currentIndex = 0; final int N = getChildCount(); @@ -5677,24 +5216,6 @@ public class NotificationStackScrollLayout extends ViewGroup updateSpeedBumpIndex(speedBumpIndex, noAmbient); } - @ShadeViewRefactor(RefactorComponent.INPUT) - private boolean isTouchInView(MotionEvent ev, View view) { - if (view == null) { - return false; - } - final int height = (view instanceof ExpandableView) - ? ((ExpandableView) view).getActualHeight() - : view.getHeight(); - final int rx = (int) ev.getRawX(); - final int ry = (int) ev.getRawY(); - view.getLocationOnScreen(mTempInt2); - final int x = mTempInt2[0]; - final int y = mTempInt2[1]; - Rect rect = new Rect(x, y, x + view.getWidth(), y + height); - boolean ret = rect.contains(rx, ry); - return ret; - } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContinuousShadowDrawing() { boolean continuousShadowUpdate = mAnimationRunning @@ -5718,11 +5239,29 @@ public class NotificationStackScrollLayout extends ViewGroup @ShadeViewRefactor(RefactorComponent.INPUT) public void closeControlsIfOutsideTouch(MotionEvent ev) { - mSwipeHelper.closeControlsIfOutsideTouch(ev); + NotificationGuts guts = mNotificationGutsManager.getExposedGuts(); + NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow(); + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + View view = null; + if (guts != null && !guts.getGutsContent().isLeavebehind()) { + // Only close visible guts if they're not a leavebehind. + view = guts; + } else if (menuRow != null && menuRow.isMenuVisible() + && translatingParentView != null) { + // Checking menu + view = translatingParentView; + } + if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) { + // Touch was outside visible guts / menu notification, close what's visible + mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */, + false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + resetExposedMenuView(true /* animate */, true /* force */); + } } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - static class AnimationEvent { + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) + static class AnimationEvent { static AnimationFilter[] FILTERS = new AnimationFilter[]{ @@ -6022,8 +5561,8 @@ public class NotificationStackScrollLayout extends ViewGroup } } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private final StateListener mStateListener = new StateListener() { + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) + private final StateListener mStateListener = new StateListener() { @Override public void onStatePreChange(int oldState, int newState) { if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) { @@ -6036,9 +5575,222 @@ public class NotificationStackScrollLayout extends ViewGroup setStatusBarState(newState); } - @Override - public void onStatePostChange() { + @Override + public void onStatePostChange() { NotificationStackScrollLayout.this.onStatePostChange(); } - }; + }; + + class NotificationMenuListener implements NotificationMenuRowPlugin.OnMenuEventListener { + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public void onMenuClicked(View view, int x, int y, MenuItem item) { + if (mLongPressListener == null) { + return; + } + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + MetricsLogger.action(mContext, MetricsEvent.ACTION_TOUCH_GEAR, + row.getStatusBarNotification().getPackageName()); + } + mLongPressListener.onLongPress(view, x, y, item); + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public void onMenuReset(View row) { + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + if (translatingParentView != null && row == translatingParentView) { + mSwipeHelper.clearExposedMenuView(); + mSwipeHelper.clearTranslatingParentView(); + } + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public void onMenuShown(View row) { + if (row instanceof ExpandableNotificationRow) { + MetricsLogger.action(mContext, MetricsEvent.ACTION_REVEAL_GEAR, + ((ExpandableNotificationRow) row).getStatusBarNotification() + .getPackageName()); + } + mSwipeHelper.onMenuShown(row); + } + } + + class SwipeHelperCallback implements NotificationSwipeHelper.NotificationCallback { + @Override + public void onDismiss() { + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, + false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + } + + @Override + public void onSnooze(StatusBarNotification sbn, + NotificationSwipeActionHelper.SnoozeOption snoozeOption) { + mStatusBar.setNotificationSnoozed(sbn, snoozeOption); + } + + @Override + public boolean isExpanded() { + return NotificationStackScrollLayout.this.isExpanded(); + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public void onDragCancelled(View v) { + mFalsingManager.onNotificatonStopDismissing(); + setSwipingInProgress(false); + } + + /** + * Handles cleanup after the given {@code view} has been fully swiped out (including + * re-invoking dismiss logic in case the notification has not made its way out yet). + */ + @Override + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + public void onChildDismissed(View view) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + if (!row.isDismissed()) { + handleChildViewDismissed(view); + } + ViewGroup transientContainer = row.getTransientContainer(); + if (transientContainer != null) { + transientContainer.removeTransientView(view); + } + } + + /** + * Starts up notification dismiss and tells the notification, if any, to remove itself from + * layout. + * + * @param view view (e.g. notification) to dismiss from the layout + */ + + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + public void handleChildViewDismissed(View view) { + if (mDismissAllInProgress) { + return; + } + + boolean isBlockingHelperShown = false; + + setSwipingInProgress(false); + if (mDragAnimPendingChildren.contains(view)) { + // We start the swipe and finish it in the same frame; we don't want a drag + // animation. + mDragAnimPendingChildren.remove(view); + } + mAmbientState.onDragFinished(view); + updateContinuousShadowDrawing(); + + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + if (row.isHeadsUp()) { + mHeadsUpManager.addSwipedOutNotification( + row.getStatusBarNotification().getKey()); + } + isBlockingHelperShown = + row.performDismissWithBlockingHelper(false /* fromAccessibility */); + } + + if (!isBlockingHelperShown) { + mSwipedOutViews.add(view); + } + mFalsingManager.onNotificationDismissed(); + if (mFalsingManager.shouldEnforceBouncer()) { + mStatusBar.executeRunnableDismissingKeyguard( + null, + null /* cancelAction */, + false /* dismissShade */, + true /* afterKeyguardGone */, + false /* deferred */); + } + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public boolean isAntiFalsingNeeded() { + return onKeyguard(); + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public View getChildAtPosition(MotionEvent ev) { + View child = NotificationStackScrollLayout.this.getChildAtPosition(ev.getX(), + ev.getY()); + if (child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + ExpandableNotificationRow parent = row.getNotificationParent(); + if (parent != null && parent.areChildrenExpanded() + && (parent.areGutsExposed() + || mSwipeHelper.getExposedMenuView() == parent + || (parent.getNotificationChildren().size() == 1 + && parent.isClearable()))) { + // In this case the group is expanded and showing the menu for the + // group, further interaction should apply to the group, not any + // child notifications so we use the parent of the child. We also do the same + // if we only have a single child. + child = parent; + } + } + return child; + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public void onBeginDrag(View v) { + mFalsingManager.onNotificatonStartDismissing(); + setSwipingInProgress(true); + mAmbientState.onBeginDrag(v); + updateContinuousShadowDrawing(); + if (mAnimationsEnabled && (mIsExpanded || !isPinnedHeadsUp(v))) { + mDragAnimPendingChildren.add(v); + mNeedsAnimation = true; + } + requestChildrenUpdate(); + } + + @Override + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) + public void onChildSnappedBack(View animView, float targetLeft) { + mAmbientState.onDragFinished(animView); + updateContinuousShadowDrawing(); + if (!mDragAnimPendingChildren.contains(animView)) { + if (mAnimationsEnabled) { + mSnappedBackChildren.add(animView); + mNeedsAnimation = true; + } + requestChildrenUpdate(); + } else { + // We start the swipe and snap back in the same frame, we don't want any animation + mDragAnimPendingChildren.remove(animView); + } + NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow(); + if (menuRow != null && targetLeft == 0) { + menuRow.resetMenu(); + mSwipeHelper.clearCurrentMenuRow(); + } + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public boolean updateSwipeProgress(View animView, boolean dismissable, + float swipeProgress) { + // Returning true prevents alpha fading. + return !mFadeNotificationsOnDismiss; + } + + @Override + @ShadeViewRefactor(RefactorComponent.INPUT) + public float getFalsingThresholdFactor() { + return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f; + } + + @Override + public boolean canChildBeDismissed(View v) { + return NotificationStackScrollLayout.this.canChildBeDismissed(v); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java new file mode 100644 index 000000000000..028957d233ff --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -0,0 +1,424 @@ +/* + * 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 Licen + */ + + +package com.android.systemui.statusbar.notification.stack; + +import android.animation.Animator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.SwipeHelper; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.statusbar.notification.ShadeViewRefactor; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.ExpandableView; + +@ShadeViewRefactor(ShadeViewRefactor.RefactorComponent.INPUT) +class NotificationSwipeHelper extends SwipeHelper + implements NotificationSwipeActionHelper { + @VisibleForTesting + protected static final long COVER_MENU_DELAY = 4000; + private static final String TAG = "NotificationSwipeHelper"; + private final Runnable mFalsingCheck; + private View mTranslatingParentView; + private View mMenuExposedView; + private final NotificationCallback mCallback; + private final NotificationMenuRowPlugin.OnMenuEventListener mMenuListener; + + private static final long SWIPE_MENU_TIMING = 200; + + private NotificationMenuRowPlugin mCurrMenuRow; + + public NotificationSwipeHelper(int swipeDirection, NotificationCallback callback, + Context context, NotificationMenuRowPlugin.OnMenuEventListener menuListener) { + super(swipeDirection, callback, context); + mMenuListener = menuListener; + mCallback = callback; + mFalsingCheck = new Runnable() { + @Override + public void run() { + resetExposedMenuView(true /* animate */, true /* force */); + } + }; + } + + public View getTranslatingParentView() { + return mTranslatingParentView; + } + + public void clearTranslatingParentView() { setTranslatingParentView(null); } + + @VisibleForTesting + protected void setTranslatingParentView(View view) { mTranslatingParentView = view; }; + + public void setExposedMenuView(View view) { + mMenuExposedView = view; + } + + public void clearExposedMenuView() { setExposedMenuView(null); } + + public void clearCurrentMenuRow() { setCurrentMenuRow(null); } + + public View getExposedMenuView() { + return mMenuExposedView; + } + + public void setCurrentMenuRow(NotificationMenuRowPlugin menuRow) { + mCurrMenuRow = menuRow; + } + + public NotificationMenuRowPlugin getCurrentMenuRow() { return mCurrMenuRow; } + + @VisibleForTesting + protected Handler getHandler() { return mHandler; } + + @VisibleForTesting + protected Runnable getFalsingCheck() { return mFalsingCheck; }; + + @Override + public void onDownUpdate(View currView, MotionEvent ev) { + mTranslatingParentView = currView; + NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); + if (menuRow != null) { + menuRow.onTouchStart(); + } + clearCurrentMenuRow(); + getHandler().removeCallbacks(getFalsingCheck()); + + // Slide back any notifications that might be showing a menu + resetExposedMenuView(true /* animate */, false /* force */); + + if (currView instanceof ExpandableNotificationRow) { + initializeRow((ExpandableNotificationRow) currView); + } + } + + @VisibleForTesting + protected void initializeRow(ExpandableNotificationRow row) { + if (row.getEntry().hasFinishedInitialization()) { + mCurrMenuRow = row.createMenu(); + mCurrMenuRow.setMenuClickListener(mMenuListener); + mCurrMenuRow.onTouchStart(); + } + } + + private boolean swipedEnoughToShowMenu(NotificationMenuRowPlugin menuRow) { + return !swipedFarEnough() && menuRow.isSwipedEnoughToShowMenu(); + } + + @Override + public void onMoveUpdate(View view, MotionEvent ev, float translation, float delta) { + getHandler().removeCallbacks(getFalsingCheck()); + NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); + if (menuRow != null) { + menuRow.onTouchMove(delta); + } + } + + @Override + public boolean handleUpEvent(MotionEvent ev, View animView, float velocity, + float translation) { + NotificationMenuRowPlugin menuRow = getCurrentMenuRow(); + if (menuRow != null) { + menuRow.onTouchEnd(); + handleMenuRowSwipe(ev, animView, velocity, menuRow); + return true; + } + return false; + } + + @VisibleForTesting + protected void handleMenuRowSwipe(MotionEvent ev, View animView, float velocity, + NotificationMenuRowPlugin menuRow) { + if (!menuRow.shouldShowMenu()) { + // If the menu should not be shown, then there is no need to check if the a swipe + // should result in a snapping to the menu. As a result, just check if the swipe + // was enough to dismiss the notification. + if (isDismissGesture(ev)) { + dismiss(animView, velocity); + } else { + snapClosed(animView, velocity); + menuRow.onSnapClosed(); + } + return; + } + + if (menuRow.isSnappedAndOnSameSide()) { + // Menu was snapped to previously and we're on the same side + handleSwipeFromSnap(ev, animView, velocity, menuRow); + } else { + // Menu has not been snapped, or was snapped previously but is now on + // the opposite side. + handleSwipeFromNonSnap(ev, animView, velocity, menuRow); + } + } + + private void handleSwipeFromNonSnap(MotionEvent ev, View animView, float velocity, + NotificationMenuRowPlugin menuRow) { + boolean isDismissGesture = isDismissGesture(ev); + final boolean gestureTowardsMenu = menuRow.isTowardsMenu(velocity); + final boolean gestureFastEnough = getEscapeVelocity() <= Math.abs(velocity); + + final double timeForGesture = ev.getEventTime() - ev.getDownTime(); + final boolean showMenuForSlowOnGoing = !menuRow.canBeDismissed() + && timeForGesture >= SWIPE_MENU_TIMING; + + if (!isFalseGesture(ev) + && (swipedEnoughToShowMenu(menuRow) + && (!gestureFastEnough || showMenuForSlowOnGoing)) + || (gestureTowardsMenu && !isDismissGesture)) { + // Menu has not been snapped to previously and this is menu revealing gesture + snapOpen(animView, menuRow.getMenuSnapTarget(), velocity); + menuRow.onSnapOpen(); + } else if (isDismissGesture(ev) && !gestureTowardsMenu) { + dismiss(animView, velocity); + menuRow.onDismiss(); + } else { + snapClosed(animView, velocity); + menuRow.onSnapClosed(); + } + } + + private void handleSwipeFromSnap(MotionEvent ev, View animView, float velocity, + NotificationMenuRowPlugin menuRow) { + boolean isDismissGesture = isDismissGesture(ev); + + final boolean withinSnapMenuThreshold = + menuRow.isWithinSnapMenuThreshold(); + + if (withinSnapMenuThreshold && !isDismissGesture) { + // Haven't moved enough to unsnap from the menu + menuRow.onSnapOpen(); + snapOpen(animView, menuRow.getMenuSnapTarget(), velocity); + } else if (isDismissGesture && !menuRow.shouldSnapBack()) { + // Only dismiss if we're not moving towards the menu + dismiss(animView, velocity); + menuRow.onDismiss(); + } else { + snapClosed(animView, velocity); + menuRow.onSnapClosed(); + } + } + + @Override + public void dismissChild(final View view, float velocity, + boolean useAccelerateInterpolator) { + superDismissChild(view, velocity, useAccelerateInterpolator); + if (mCallback.isExpanded()) { + // We don't want to quick-dismiss when it's a heads up as this might lead to closing + // of the panel early. + mCallback.handleChildViewDismissed(view); + } + mCallback.onDismiss(); + handleMenuCoveredOrDismissed(); + } + + @VisibleForTesting + protected void superDismissChild(final View view, float velocity, boolean useAccelerateInterpolator) { + super.dismissChild(view, velocity, useAccelerateInterpolator); + } + + @VisibleForTesting + protected void superSnapChild(final View animView, final float targetLeft, float velocity) { + super.snapChild(animView, targetLeft, velocity); + } + + @Override + public void snapChild(final View animView, final float targetLeft, float velocity) { + superSnapChild(animView, targetLeft, velocity); + mCallback.onDragCancelled(animView); + if (targetLeft == 0) { + handleMenuCoveredOrDismissed(); + } + } + + @Override + public void snooze(StatusBarNotification sbn, SnoozeOption snoozeOption) { + mCallback.onSnooze(sbn, snoozeOption); + } + + @VisibleForTesting + protected void handleMenuCoveredOrDismissed() { + View exposedMenuView = getExposedMenuView(); + if (exposedMenuView != null && exposedMenuView == mTranslatingParentView) { + clearExposedMenuView(); + } + } + + @VisibleForTesting + protected Animator superGetViewTranslationAnimator(View v, float target, + ValueAnimator.AnimatorUpdateListener listener) { + return super.getViewTranslationAnimator(v, target, listener); + } + + @Override + public Animator getViewTranslationAnimator(View v, float target, + ValueAnimator.AnimatorUpdateListener listener) { + if (v instanceof ExpandableNotificationRow) { + return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener); + } else { + return superGetViewTranslationAnimator(v, target, listener); + } + } + + @Override + public void setTranslation(View v, float translate) { + if (v instanceof ExpandableNotificationRow) { + ((ExpandableNotificationRow) v).setTranslation(translate); + } else { + Log.wtf(TAG, "setTranslation should only be called on an ExpandableNotificationRow."); + } + } + + @Override + public float getTranslation(View v) { + if (v instanceof ExpandableNotificationRow) { + return ((ExpandableNotificationRow) v).getTranslation(); + } + else { + Log.wtf(TAG, "getTranslation should only be called on an ExpandableNotificationRow."); + return 0f; + } + } + + @Override + public boolean swipedFastEnough(float translation, float viewSize) { + return swipedFastEnough(); + } + + @Override + @VisibleForTesting + protected boolean swipedFastEnough() { + return super.swipedFastEnough(); + } + + @Override + public boolean swipedFarEnough(float translation, float viewSize) { + return swipedFarEnough(); + } + + @Override + @VisibleForTesting + protected boolean swipedFarEnough() { + return super.swipedFarEnough(); + } + + @Override + public void dismiss(View animView, float velocity) { + dismissChild(animView, velocity, + !swipedFastEnough() /* useAccelerateInterpolator */); + } + + @Override + public void snapOpen(View animView, int targetLeft, float velocity) { + snapChild(animView, targetLeft, velocity); + } + + @VisibleForTesting + protected void snapClosed(View animView, float velocity) { + snapChild(animView, 0, velocity); + } + + @Override + @VisibleForTesting + protected float getEscapeVelocity() { + return super.getEscapeVelocity(); + } + + @Override + public float getMinDismissVelocity() { + return getEscapeVelocity(); + } + + public void onMenuShown(View animView) { + setExposedMenuView(getTranslatingParentView()); + mCallback.onDragCancelled(animView); + Handler handler = getHandler(); + + // If we're on the lockscreen we want to false this. + if (mCallback.isAntiFalsingNeeded()) { + handler.removeCallbacks(getFalsingCheck()); + handler.postDelayed(getFalsingCheck(), COVER_MENU_DELAY); + } + } + + @VisibleForTesting + protected boolean shouldResetMenu(boolean force) { + if (mMenuExposedView == null + || (!force && mMenuExposedView == mTranslatingParentView)) { + // If no menu is showing or it's showing for this view we do nothing. + return false; + } + return true; + } + + public void resetExposedMenuView(boolean animate, boolean force) { + if (!shouldResetMenu(force)) { + return; + } + final View prevMenuExposedView = getExposedMenuView(); + if (animate) { + Animator anim = getViewTranslationAnimator(prevMenuExposedView, + 0 /* leftTarget */, null /* updateListener */); + if (anim != null) { + anim.start(); + } + } else if (prevMenuExposedView instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) prevMenuExposedView; + if (!row.isRemoved()) { + row.resetTranslation(); + } + } + clearExposedMenuView(); + } + + public static boolean isTouchInView(MotionEvent ev, View view) { + if (view == null) { + return false; + } + final int height = (view instanceof ExpandableView) + ? ((ExpandableView) view).getActualHeight() + : view.getHeight(); + final int rx = (int) ev.getRawX(); + final int ry = (int) ev.getRawY(); + int[] temp = new int[2]; + view.getLocationOnScreen(temp); + final int x = temp[0]; + final int y = temp[1]; + Rect rect = new Rect(x, y, x + view.getWidth(), y + height); + boolean ret = rect.contains(rx, ry); + return ret; + } + + public interface NotificationCallback extends SwipeHelper.Callback{ + boolean isExpanded(); + + void handleChildViewDismissed(View view); + + void onSnooze(StatusBarNotification sbn, SnoozeOption snoozeOption); + + void onDismiss(); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java index 09c19319429b..da59450af4df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java @@ -79,13 +79,14 @@ public class NonPhoneDependencyTest extends SysuiTestCase { Dependency.get(NotificationLockscreenUserManager.class); NotificationViewHierarchyManager viewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); + NotificationGroupManager groupManager = Dependency.get(NotificationGroupManager.class); when(mPresenter.getNotificationLockscreenUserManager()).thenReturn(lockscreenUserManager); - when(mPresenter.getGroupManager()).thenReturn( - Dependency.get(NotificationGroupManager.class)); + when(mPresenter.getGroupManager()).thenReturn(groupManager); entryManager.setUpWithPresenter(mPresenter, mListContainer, mEntryManagerCallback, mHeadsUpManager); + groupManager.setHeadsUpManager(mHeadsUpManager); gutsManager.setUpWithPresenter(mPresenter, mListContainer, mCheckSaveListener, mOnClickListener); notificationLogger.setUpWithEntryManager(entryManager, mListContainer); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java new file mode 100644 index 000000000000..b5f67c06b2d1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -0,0 +1,511 @@ +/* + * 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.statusbar.notification.stack; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockitoSession; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.animation.Animator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.os.IPowerManager; +import android.os.Looper; +import android.os.PowerManager; +import android.service.notification.StatusBarNotification; +import android.support.test.annotation.UiThreadTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.testing.TestableLooper.RunWithLooper; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.MotionEvent; + +import com.android.systemui.SwipeHelper; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.notification.row.NotificationMenuRow; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; + +/** + * Tests for {@link NotificationSwipeHelper}. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class NotificationSwipeHelperTest extends SysuiTestCase { + + private NotificationSwipeHelper mSwipeHelper; + private NotificationSwipeHelper.NotificationCallback mCallback; + private NotificationMenuRowPlugin.OnMenuEventListener mListener; + private View mView; + private MotionEvent mEvent; + private NotificationMenuRowPlugin mMenuRow; + private Handler mHandler; + private ExpandableNotificationRow mNotificationRow; + private Runnable mFalsingCheck; + + @Rule public MockitoRule mockito = MockitoJUnit.rule(); + + @Before + @UiThreadTest + public void setUp() throws Exception { + mCallback = mock(NotificationSwipeHelper.NotificationCallback.class); + mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class); + mSwipeHelper = spy(new NotificationSwipeHelper(SwipeHelper.X, mCallback, mContext, mListener)); + mView = mock(View.class); + mEvent = mock(MotionEvent.class); + mMenuRow = mock(NotificationMenuRowPlugin.class); + mNotificationRow = mock(ExpandableNotificationRow.class); + mHandler = mock(Handler.class); + mFalsingCheck = mock(Runnable.class); + } + + @Test + public void testSetExposedMenuView() { + assertEquals("intialized with null exposed menu view", null, + mSwipeHelper.getExposedMenuView()); + mSwipeHelper.setExposedMenuView(mView); + assertEquals("swipe helper has correct exposedMenuView after setExposedMenuView to a view", + mView, mSwipeHelper.getExposedMenuView()); + mSwipeHelper.setExposedMenuView(null); + assertEquals("swipe helper has null exposedMenuView after setExposedMenuView to null", + null, mSwipeHelper.getExposedMenuView()); + } + + @Test + public void testClearExposedMenuView() { + doNothing().when(mSwipeHelper).setExposedMenuView(mView); + mSwipeHelper.clearExposedMenuView(); + verify(mSwipeHelper, times(1)).setExposedMenuView(null); + } + + @Test + public void testGetTranslatingParentView() { + assertEquals("intialized with null translating parent view", null, + mSwipeHelper.getTranslatingParentView()); + mSwipeHelper.setTranslatingParentView(mView); + assertEquals("has translating parent view after setTranslatingParentView with a view", + mView, mSwipeHelper.getTranslatingParentView()); + } + + @Test + public void testClearTranslatingParentView() { + doNothing().when(mSwipeHelper).setTranslatingParentView(null); + mSwipeHelper.clearTranslatingParentView(); + verify(mSwipeHelper, times(1)).setTranslatingParentView(null); + } + + @Test + public void testSetCurrentMenuRow() { + assertEquals("currentMenuRow initializes to null", null, + mSwipeHelper.getCurrentMenuRow()); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + assertEquals("currentMenuRow set correctly after setCurrentMenuRow", mMenuRow, + mSwipeHelper.getCurrentMenuRow()); + mSwipeHelper.setCurrentMenuRow(null); + assertEquals("currentMenuRow set to null after setCurrentMenuRow to null", + null, mSwipeHelper.getCurrentMenuRow()); + } + + @Test + public void testClearCurrentMenuRow() { + doNothing().when(mSwipeHelper).setCurrentMenuRow(null); + mSwipeHelper.clearCurrentMenuRow(); + verify(mSwipeHelper, times(1)).setCurrentMenuRow(null); + } + + @Test + public void testOnDownUpdate_ExpandableNotificationRow() { + when(mSwipeHelper.getHandler()).thenReturn(mHandler); + when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck); + doNothing().when(mSwipeHelper).resetExposedMenuView(true, false); + doNothing().when(mSwipeHelper).clearCurrentMenuRow(); + doNothing().when(mSwipeHelper).initializeRow(any()); + + mSwipeHelper.onDownUpdate(mNotificationRow, mEvent); + + verify(mSwipeHelper, times(1)).clearCurrentMenuRow(); + verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); + verify(mSwipeHelper, times(1)).resetExposedMenuView(true, false); + verify(mSwipeHelper, times(1)).initializeRow(mNotificationRow); + } + + @Test + public void testOnDownUpdate_notExpandableNotificationRow() { + when(mSwipeHelper.getHandler()).thenReturn(mHandler); + when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck); + doNothing().when(mSwipeHelper).resetExposedMenuView(true, false); + doNothing().when(mSwipeHelper).clearCurrentMenuRow(); + doNothing().when(mSwipeHelper).initializeRow(any()); + + mSwipeHelper.onDownUpdate(mView, mEvent); + + verify(mSwipeHelper, times(1)).clearCurrentMenuRow(); + verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); + verify(mSwipeHelper, times(1)).resetExposedMenuView(true, false); + verify(mSwipeHelper, times(0)).initializeRow(any()); + } + + @Test + public void testOnMoveUpdate_menuRow() { + when(mSwipeHelper.getCurrentMenuRow()).thenReturn(mMenuRow); + when(mSwipeHelper.getHandler()).thenReturn(mHandler); + when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck); + + mSwipeHelper.onMoveUpdate(mView, mEvent, 0, 10); + + verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); + verify(mMenuRow, times(1)).onTouchMove(10); + } + + @Test + public void testOnMoveUpdate_noMenuRow() { + when(mSwipeHelper.getHandler()).thenReturn(mHandler); + when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck); + + mSwipeHelper.onMoveUpdate(mView, mEvent, 0, 10); + + verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); + } + + @Test + public void testHandleUpEvent_noMenuRow() { + assertFalse("Menu row does not exist", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + } + + @Test + public void testHandleUpEvent_menuRow() { + when(mSwipeHelper.getCurrentMenuRow()).thenReturn(mMenuRow); + doNothing().when(mSwipeHelper).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow); + } + + @Test + public void testDismissChild_notExpanded() { + when(mCallback.isExpanded()).thenReturn(false); + doNothing().when(mSwipeHelper).superDismissChild(mView, 0, false); + doNothing().when(mSwipeHelper).handleMenuCoveredOrDismissed(); + + mSwipeHelper.dismissChild(mView, 0, false); + + verify(mSwipeHelper, times(1)).superDismissChild(mView, 0, false); + verify(mCallback, times(0)).handleChildViewDismissed(mView); + verify(mCallback, times(1)).onDismiss(); + verify(mSwipeHelper, times(1)).handleMenuCoveredOrDismissed(); + } + + @Test + public void testSnapchild_targetIsZero() { + doNothing().when(mSwipeHelper).superSnapChild(mView, 0, 0); + mSwipeHelper.snapChild(mView, 0, 0); + + verify(mCallback, times(1)).onDragCancelled(mView); + verify(mSwipeHelper, times(1)).superSnapChild(mView, 0, 0); + verify(mSwipeHelper, times(1)).handleMenuCoveredOrDismissed(); + } + + + @Test + public void testSnapchild_targetNotZero() { + doNothing().when(mSwipeHelper).superSnapChild(mView, 10, 0); + mSwipeHelper.snapChild(mView, 10, 0); + + verify(mCallback, times(1)).onDragCancelled(mView); + verify(mSwipeHelper, times(1)).superSnapChild(mView, 10, 0); + verify(mSwipeHelper, times(0)).handleMenuCoveredOrDismissed(); + } + + @Test + public void testSnooze() { + StatusBarNotification sbn = mock(StatusBarNotification.class); + SnoozeOption snoozeOption = mock(SnoozeOption.class); + mSwipeHelper.snooze(sbn, snoozeOption); + verify(mCallback, times(1)).onSnooze(sbn, snoozeOption); + } + + @Test + public void testGetViewTranslationAnimator_notExpandableNotificationRow() { + Animator animator = mock(Animator.class); + AnimatorUpdateListener listener = mock(AnimatorUpdateListener.class); + doReturn(animator).when(mSwipeHelper).superGetViewTranslationAnimator(mView, 0, listener); + + assertEquals("returns the correct animator from super", animator, + mSwipeHelper.getViewTranslationAnimator(mView, 0, listener)); + + verify(mSwipeHelper, times(1)).superGetViewTranslationAnimator(mView, 0, listener); + } + + @Test + public void testGetViewTranslationAnimator_expandableNotificationRow() { + Animator animator = mock(Animator.class); + AnimatorUpdateListener listener = mock(AnimatorUpdateListener.class); + doReturn(animator).when(mNotificationRow).getTranslateViewAnimator(0, listener); + + assertEquals("returns the correct animator from super when view is an ENR", animator, + mSwipeHelper.getViewTranslationAnimator(mNotificationRow, 0, listener)); + + verify(mNotificationRow, times(1)).getTranslateViewAnimator(0, listener); + } + + @Test + public void testSetTranslation() { + mSwipeHelper.setTranslation(mNotificationRow, 0); + verify(mNotificationRow, times(1)).setTranslation(0); + } + + @Test + public void testGetTranslation() { + doReturn(30f).when(mNotificationRow).getTranslation(); + + assertEquals("Returns getTranslation for the ENR", + mSwipeHelper.getTranslation(mNotificationRow), 30f); + + verify(mNotificationRow, times(1)).getTranslation(); + } + + @Test + public void testDismiss() { + doNothing().when(mSwipeHelper).dismissChild(mView, 0, true); + doReturn(false).when(mSwipeHelper).swipedFastEnough(); + + mSwipeHelper.dismiss(mView, 0); + + verify(mSwipeHelper, times(1)).swipedFastEnough(); + verify(mSwipeHelper, times(1)).dismissChild(mView, 0, true); + } + + @Test + public void testSnapOpen() { + doNothing().when(mSwipeHelper).snapChild(mView, 30, 0); + + mSwipeHelper.snapOpen(mView, 30, 0); + + verify(mSwipeHelper, times(1)).snapChild(mView, 30, 0); + } + + @Test + public void testSnapClosed() { + doNothing().when(mSwipeHelper).snapChild(mView, 0, 0); + + mSwipeHelper.snapClosed(mView, 0); + + verify(mSwipeHelper, times(1)).snapChild(mView, 0, 0); + } + + @Test + public void testGetMinDismissVelocity() { + doReturn(30f).when(mSwipeHelper).getEscapeVelocity(); + + assertEquals("Returns getEscapeVelocity", 30f, mSwipeHelper.getMinDismissVelocity()); + } + + @Test + public void onMenuShown_noAntiFalsing() { + doNothing().when(mSwipeHelper).setExposedMenuView(mView); + doReturn(mView).when(mSwipeHelper).getTranslatingParentView(); + doReturn(mHandler).when(mSwipeHelper).getHandler(); + doReturn(false).when(mCallback).isAntiFalsingNeeded(); + doReturn(mFalsingCheck).when(mSwipeHelper).getFalsingCheck(); + + mSwipeHelper.onMenuShown(mView); + + verify(mSwipeHelper, times(1)).setExposedMenuView(mView); + verify(mCallback, times(1)).onDragCancelled(mView); + verify(mCallback, times(1)).isAntiFalsingNeeded(); + + verify(mHandler, times(0)).removeCallbacks(mFalsingCheck); + verify(mHandler, times(0)).postDelayed(mFalsingCheck, mSwipeHelper.COVER_MENU_DELAY); + } + + @Test + public void onMenuShown_antiFalsing() { + doNothing().when(mSwipeHelper).setExposedMenuView(mView); + doReturn(mView).when(mSwipeHelper).getTranslatingParentView(); + doReturn(mHandler).when(mSwipeHelper).getHandler(); + doReturn(true).when(mCallback).isAntiFalsingNeeded(); + doReturn(mFalsingCheck).when(mSwipeHelper).getFalsingCheck(); + + mSwipeHelper.onMenuShown(mView); + + verify(mSwipeHelper, times(1)).setExposedMenuView(mView); + verify(mCallback, times(1)).onDragCancelled(mView); + verify(mCallback, times(1)).isAntiFalsingNeeded(); + + verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); + verify(mHandler, times(1)).postDelayed(mFalsingCheck, mSwipeHelper.COVER_MENU_DELAY); + } + + @Test + public void testResetExposedMenuView_noReset() { + doReturn(false).when(mSwipeHelper).shouldResetMenu(false); + doNothing().when(mSwipeHelper).clearExposedMenuView(); + + mSwipeHelper.resetExposedMenuView(false, false); + + verify(mSwipeHelper, times(1)).shouldResetMenu(false); + + // should not clear exposed menu row + verify(mSwipeHelper, times(0)).clearExposedMenuView(); + } + + @Test + public void testResetExposedMenuView_animate() { + Animator animator = mock(Animator.class); + + doReturn(true).when(mSwipeHelper).shouldResetMenu(false); + doReturn(mNotificationRow).when(mSwipeHelper).getExposedMenuView(); + doReturn(false).when(mNotificationRow).isRemoved(); + doReturn(animator).when(mSwipeHelper).getViewTranslationAnimator(mNotificationRow, 0, null); + doNothing().when(mSwipeHelper).clearExposedMenuView(); + + mSwipeHelper.resetExposedMenuView(true, false); + + verify(mSwipeHelper, times(1)).shouldResetMenu(false); + + // should retrieve and start animator + verify(mSwipeHelper, times(1)).getViewTranslationAnimator(mNotificationRow, 0, null); + verify(animator, times(1)).start(); + + // should not reset translation on row directly + verify(mNotificationRow, times(0)).resetTranslation(); + + // should clear exposed menu row + verify(mSwipeHelper, times(1)).clearExposedMenuView(); + } + + + @Test + public void testResetExposedMenuView_noAnimate() { + Animator animator = mock(Animator.class); + + doReturn(true).when(mSwipeHelper).shouldResetMenu(false); + doReturn(mNotificationRow).when(mSwipeHelper).getExposedMenuView(); + doReturn(false).when(mNotificationRow).isRemoved(); + doReturn(animator).when(mSwipeHelper).getViewTranslationAnimator(mNotificationRow, 0, null); + doNothing().when(mSwipeHelper).clearExposedMenuView(); + + mSwipeHelper.resetExposedMenuView(false, false); + + verify(mSwipeHelper, times(1)).shouldResetMenu(false); + + // should not retrieve and start animator + verify(mSwipeHelper, times(0)).getViewTranslationAnimator(mNotificationRow, 0, null); + verify(animator, times(0)).start(); + + // should reset translation on row directly + verify(mNotificationRow, times(1)).resetTranslation(); + + // should clear exposed menu row + verify(mSwipeHelper, times(1)).clearExposedMenuView(); + } + + @Test + public void testIsTouchInView() { + assertEquals("returns false when view is null", false, + NotificationSwipeHelper.isTouchInView(mEvent, null)); + + doReturn(5f).when(mEvent).getRawX(); + doReturn(10f).when(mEvent).getRawY(); + + doReturn(20).when(mView).getWidth(); + doReturn(20).when(mView).getHeight(); + + Answer answer = (Answer) invocation -> { + int[] arr = invocation.getArgument(0); + arr[0] = 0; + arr[1] = 0; + return null; + }; + doAnswer(answer).when(mView).getLocationOnScreen(any()); + + assertTrue("Touch is within the view", + mSwipeHelper.isTouchInView(mEvent, mView)); + + doReturn(50f).when(mEvent).getRawX(); + + assertFalse("Touch is not within the view", + mSwipeHelper.isTouchInView(mEvent, mView)); + } + + @Test + public void testIsTouchInView_expandable() { + assertEquals("returns false when view is null", false, + NotificationSwipeHelper.isTouchInView(mEvent, null)); + + doReturn(5f).when(mEvent).getRawX(); + doReturn(10f).when(mEvent).getRawY(); + + doReturn(20).when(mNotificationRow).getWidth(); + doReturn(20).when(mNotificationRow).getActualHeight(); + + Answer answer = (Answer) invocation -> { + int[] arr = invocation.getArgument(0); + arr[0] = 0; + arr[1] = 0; + return null; + }; + doAnswer(answer).when(mNotificationRow).getLocationOnScreen(any()); + + assertTrue("Touch is within the view", + mSwipeHelper.isTouchInView(mEvent, mNotificationRow)); + + doReturn(50f).when(mEvent).getRawX(); + + assertFalse("Touch is not within the view", + mSwipeHelper.isTouchInView(mEvent, mNotificationRow)); + } +} diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 90c10fdcbfde..39472955dfa8 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6447,7 +6447,7 @@ message MetricsEvent { // 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 e41a09ef672e..a1989e51ee51 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; 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.IpConnectivityLog; 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 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub "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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler); - mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties); + mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties); registerPrivateDnsSettingsCallbacks(); } @@ -912,7 +916,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub }; /** + * 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 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @Override public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) { + if (disallowedBecauseSystemCaller()) { + return false; + } enforceChangePermission(); if (mProtectedNetworks.contains(networkType)) { enforceConnectivityInternalPermission(); @@ -1563,7 +1584,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } nai.clearLingerState(); if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) { - removeDataActivityTracking(nai); + updateDataActivityTracking(null /* newNetwork */, nai); notifyLockdownVpn(nai); ensureNetworkTransitionWakelock(nai.name()); } @@ -2581,7 +2614,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } } 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 1d163eed00a4..de930f794e50 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -161,6 +161,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub @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 class NetworkManagementService extends INetworkManagementService.Stub 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 class NetworkManagementService extends INetworkManagementService.Stub 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 class NetworkManagementService extends INetworkManagementService.Stub 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 class NetworkManagementService extends INetworkManagementService.Stub 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 @@ public class NetworkManagementService extends INetworkManagementService.Stub 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 class NetworkManagementService extends INetworkManagementService.Stub 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 d505a77c9192..21f54dd33d3e 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -329,6 +329,12 @@ class StorageManagerService extends IStorageManager.Stub @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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub addInternalVolumeLocked(); } + mVisibleVols.clear(); + try { mVold.reset(); @@ -1466,7 +1470,7 @@ class StorageManagerService extends IStorageManager.Stub = 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 @@ class StorageManagerService extends IStorageManager.Stub } @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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub 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 @@ class StorageManagerService extends IStorageManager.Stub 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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 692f9cfaa833..a3a5ac8b5999 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.InputStreamReader; 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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!"); @@ -9431,9 +9435,10 @@ public class ActivityManagerService extends IActivityManager.Stub mBatteryStatsService.noteWakupAlarm(sourcePkg, sourceUid, workSource, tag); if (workSource != null) { - StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, workSource, tag); + StatsLog.write(StatsLog.WAKEUP_ALARM_OCCURRED, workSource, tag, sourcePkg); } else { - StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, sourceUid, null, tag); + StatsLog.write_non_chained(StatsLog.WAKEUP_ALARM_OCCURRED, sourceUid, null, tag, + sourcePkg); } } diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index add9f2a7e9d8..36261b505a94 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -1960,10 +1960,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId); if (task == null) { Slog.d(TAG, "Could not find task for id: "+ taskId); + SafeActivityOptions.abort(options); return; } if (getLockTaskController().isLockTaskModeViolation(task)) { Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode"); + SafeActivityOptions.abort(options); return; } ActivityOptions realOptions = options != null @@ -1983,7 +1985,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } finally { Binder.restoreCallingIdentity(origId); } - SafeActivityOptions.abort(options); } boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid, diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 66c7c437284b..f0ff570e385b 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 @@ public class AudioService extends IAudioService.Stub @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 class AudioService extends IAudioService.Stub 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 @@ public class AudioService extends IAudioService.Stub } 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 @@ public class AudioService extends IAudioService.Stub // - 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/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index 0f68c6889680..87cf9c434fc0 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -39,6 +39,7 @@ import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; +import android.provider.Settings; import android.util.Slog; import com.android.internal.R; @@ -156,9 +157,18 @@ public class BiometricService extends SystemService { } else if (mCurrentModality == BIOMETRIC_IRIS) { Slog.w(TAG, "Unsupported modality"); } else if (mCurrentModality == BIOMETRIC_FACE) { - mFaceService.authenticateFromService(true /* requireConfirmation */, token, - sessionId, userId, receiver, flags, opPackageName, bundle, - dialogReceiver, callingUid, callingPid, callingUserId); + // If the user disabled face for apps, return ERROR_HW_UNAVAILABLE + if (isFaceEnabledForApps()) { + receiver.onError(0 /* deviceId */, + BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, + FaceManager.getErrorString(getContext(), + BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */)); + } else { + mFaceService.authenticateFromService(true /* requireConfirmation */, + token, sessionId, userId, receiver, flags, opPackageName, + bundle, dialogReceiver, callingUid, callingPid, callingUserId); + } } else { Slog.w(TAG, "Unsupported modality"); } @@ -168,6 +178,15 @@ public class BiometricService extends SystemService { }); } + private boolean isFaceEnabledForApps() { + // TODO: maybe cache this and eliminate duplicated code with KeyguardUpdateMonitor + return Settings.Secure.getIntForUser( + getContext().getContentResolver(), + Settings.Secure.FACE_UNLOCK_APP_ENABLED, + 1 /* default */, + UserHandle.USER_CURRENT) == 0; + } + @Override // Binder call public void cancelAuthentication(IBinder token, String opPackageName) throws RemoteException { diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 98c38dd41bfc..f6af52ae00a3 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -28,10 +28,11 @@ import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.IBiometricPromptReceiver; -import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback; +import android.hardware.biometrics.face.V1_0.Status; import android.hardware.face.Face; import android.hardware.face.FaceManager; import android.hardware.face.IFaceService; @@ -121,15 +122,15 @@ public class FaceService extends BiometricServiceBase { * The following methods contain common code which is shared in biometrics/common. */ @Override // Binder call - public long preEnroll(IBinder token) { + public long generateChallenge(IBinder token) { checkPermission(MANAGE_BIOMETRIC); - return startPreEnroll(token); + return startGenerateChallenge(token); } @Override // Binder call - public int postEnroll(IBinder token) { + public int revokeChallenge(IBinder token) { checkPermission(MANAGE_BIOMETRIC); - return startPostEnroll(token); + return startRevokeChallenge(token); } @Override // Binder call @@ -346,6 +347,45 @@ public class FaceService extends BiometricServiceBase { // TODO: confirm security token when we move timeout management into the HAL layer. mHandler.post(mResetFailedAttemptsForCurrentUserRunnable); } + + @Override + public int setRequireAttention(boolean requireAttention, final byte[] token) { + checkPermission(MANAGE_BIOMETRIC); + + final ArrayList<Byte> byteToken = new ArrayList<>(); + for (int i = 0; i < token.length; i++) { + byteToken.add(token[i]); + } + + int result; + try { + result = mDaemon != null ? mDaemon.setRequireAttention(requireAttention, byteToken) + : Status.INTERNAL_ERROR; + } catch (RemoteException e) { + Slog.e(getTag(), "Unable to setRequireAttention to " + requireAttention); + result = Status.INTERNAL_ERROR; + } + + return result; + } + + @Override + public boolean getRequireAttention(final byte[] token) { + checkPermission(MANAGE_BIOMETRIC); + + final ArrayList<Byte> byteToken = new ArrayList<>(); + for (int i = 0; i < token.length; i++) { + byteToken.add(token[i]); + } + + boolean result = true; + try { + result = mDaemon != null ? mDaemon.getRequireAttention(byteToken).value : true; + } catch (RemoteException e) { + Slog.e(getTag(), "Unable to getRequireAttention"); + } + return result; + } } /** @@ -779,30 +819,30 @@ public class FaceService extends BiometricServiceBase { return mDaemon; } - private long startPreEnroll(IBinder token) { + private long startGenerateChallenge(IBinder token) { IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { - Slog.w(TAG, "startPreEnroll: no face HAL!"); + Slog.w(TAG, "startGenerateChallenge: no face HAL!"); return 0; } try { return daemon.generateChallenge(CHALLENGE_TIMEOUT_SEC).value; } catch (RemoteException e) { - Slog.e(TAG, "startPreEnroll failed", e); + Slog.e(TAG, "startGenerateChallenge failed", e); } return 0; } - private int startPostEnroll(IBinder token) { + private int startRevokeChallenge(IBinder token) { IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { - Slog.w(TAG, "startPostEnroll: no face HAL!"); + Slog.w(TAG, "startRevokeChallenge: no face HAL!"); return 0; } try { return daemon.revokeChallenge(); } catch (RemoteException e) { - Slog.e(TAG, "startPostEnroll failed", e); + Slog.e(TAG, "startRevokeChallenge failed", e); } return 0; } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 2a80f0e7c291..48082b64ddfc 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 @@ public class Vpn { // 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 2b1d9196fe18..1e6bb04858a1 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 @@ public class TetheringConfiguration { } 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/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index d8d650b28dee..5698fdf439a8 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -1226,7 +1226,7 @@ public final class ContentService extends IContentService.Stub { if (userId == UserHandle.USER_ALL) { mContext.enforceCallingOrSelfPermission( - Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG); + Manifest.permission.INTERACT_ACROSS_USERS_FULL, "No access to " + uri); } else if (userId < 0) { throw new IllegalArgumentException("Invalid user: " + userId); } else if (userId != UserHandle.getCallingUserId()) { @@ -1247,7 +1247,7 @@ public final class ContentService extends IContentService.Stub { ? (Manifest.permission.INTERACT_ACROSS_USERS_FULL + " or " + Manifest.permission.INTERACT_ACROSS_USERS) : Manifest.permission.INTERACT_ACROSS_USERS_FULL; - throw new SecurityException(TAG + "Neither user " + uid + throw new SecurityException("No access to " + uri + ": neither user " + uid + " nor current process has " + permissions); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index e10827bc6101..296d7ae349bc 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 @@ public class PackageManagerService extends IPackageManager.Stub 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. } @@ -16004,12 +16004,14 @@ public class PackageManagerService extends IPackageManager.Stub } if (apkPath != null) { final VerityUtils.SetupResult result = - VerityUtils.generateApkVeritySetupData(apkPath); + VerityUtils.generateApkVeritySetupData(apkPath, null /* signaturePath */, + true /* skipSigningBlock */); if (result.isOk()) { 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/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index 9f69702911c9..8070f3add5c6 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -28,40 +28,74 @@ import android.util.Slog; import android.util.apk.ApkSignatureVerifier; import android.util.apk.ByteBufferFactory; import android.util.apk.SignatureNotFoundException; +import android.util.apk.VerityBuilder; + +import libcore.util.HexEncoding; import java.io.FileDescriptor; import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.DigestException; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import sun.security.pkcs.PKCS7; + /** Provides fsverity related operations. */ abstract public class VerityUtils { private static final String TAG = "VerityUtils"; + /** The maximum size of signature file. This is just to avoid potential abuse. */ + private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192; + private static final boolean DEBUG = false; /** - * Generates Merkle tree and fsverity metadata. + * Generates Merkle tree and fs-verity metadata. * - * @return {@code SetupResult} that contains the {@code EsetupResultCode}, and when success, the + * @return {@code SetupResult} that contains the result code, and when success, the * {@code FileDescriptor} to read all the data from. */ - public static SetupResult generateApkVeritySetupData(@NonNull String apkPath) { - if (DEBUG) Slog.d(TAG, "Trying to install apk verity to " + apkPath); + public static SetupResult generateApkVeritySetupData(@NonNull String apkPath, + String signaturePath, boolean skipSigningBlock) { + if (DEBUG) { + Slog.d(TAG, "Trying to install apk verity to " + apkPath + " with signature file " + + signaturePath); + } SharedMemory shm = null; try { - byte[] signedRootHash = ApkSignatureVerifier.getVerityRootHash(apkPath); - if (signedRootHash == null) { + byte[] signedVerityHash; + if (skipSigningBlock) { + signedVerityHash = ApkSignatureVerifier.getVerityRootHash(apkPath); + } else { + Path path = Paths.get(signaturePath); + if (Files.exists(path)) { + // TODO(112037636): fail early if the signing key is not in .fs-verity keyring. + PKCS7 pkcs7 = new PKCS7(Files.readAllBytes(path)); + signedVerityHash = pkcs7.getContentInfo().getContentBytes(); + if (DEBUG) { + Slog.d(TAG, "fs-verity measurement = " + bytesToString(signedVerityHash)); + } + } else { + signedVerityHash = null; + } + } + + if (signedVerityHash == null) { if (DEBUG) { - Slog.d(TAG, "Skip verity tree generation since there is no root hash"); + Slog.d(TAG, "Skip verity tree generation since there is no signed root hash"); } return SetupResult.skipped(); } - Pair<SharedMemory, Integer> result = generateApkVerityIntoSharedMemory(apkPath, - signedRootHash); + Pair<SharedMemory, Integer> result = generateFsVerityIntoSharedMemory(apkPath, + signaturePath, signedVerityHash, skipSigningBlock); shm = result.first; int contentSize = result.second; FileDescriptor rfd = shm.getFileDescriptor(); @@ -81,9 +115,9 @@ abstract public class VerityUtils { } /** - * {@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); } @@ -97,22 +131,114 @@ abstract public class VerityUtils { } /** + * Generates fs-verity metadata for {@code filePath} in the buffer created by {@code + * trackedBufferFactory}. The metadata contains the Merkle tree, fs-verity descriptor and + * extensions, including a PKCS#7 signature provided in {@code signaturePath}. + * + * <p>It is worthy to note that {@code trackedBufferFactory} generates a "tracked" {@code + * ByteBuffer}. The data will be used outside this method via the factory itself. + * + * @return fs-verity measurement of {@code filePath}, which is a SHA-256 of fs-verity descriptor + * and authenticated extensions. + */ + private static byte[] generateFsverityMetadata(String filePath, String signaturePath, + @NonNull TrackedShmBufferFactory trackedBufferFactory) + throws IOException, SignatureNotFoundException, SecurityException, DigestException, + NoSuchAlgorithmException { + try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) { + VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree( + file, trackedBufferFactory); + + ByteBuffer buffer = result.verityData; + buffer.position(result.merkleTreeSize); + return generateFsverityDescriptorAndMeasurement(file, result.rootHash, signaturePath, + buffer); + } + } + + /** + * Generates fs-verity descriptor including the extensions to the {@code output} and returns the + * fs-verity measurement. + * + * @return fs-verity measurement, which is a SHA-256 of fs-verity descriptor and authenticated + * extensions. + */ + private static byte[] generateFsverityDescriptorAndMeasurement( + @NonNull RandomAccessFile file, @NonNull byte[] rootHash, + @NonNull String pkcs7SignaturePath, @NonNull ByteBuffer output) + throws IOException, NoSuchAlgorithmException, DigestException { + final short kRootHashExtensionId = 1; + final short kPkcs7SignatureExtensionId = 3; + final int origPosition = output.position(); + + // For generating fs-verity file measurement, which consists of the descriptor and + // authenticated extensions (but not unauthenticated extensions and the footer). + MessageDigest md = MessageDigest.getInstance("SHA-256"); + + // 1. Generate fs-verity descriptor. + final byte[] desc = constructFsverityDescriptorNative(file.length()); + output.put(desc); + md.update(desc); + + // 2. Generate authenticated extensions. + final byte[] authExt = + constructFsverityExtensionNative(kRootHashExtensionId, rootHash.length); + output.put(authExt); + output.put(rootHash); + md.update(authExt); + md.update(rootHash); + + // 3. Generate unauthenticated extensions. + ByteBuffer header = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN); + output.putShort((short) 1); // number of unauthenticated extensions below + output.position(output.position() + 6); + + // Generate PKCS#7 extension. NB: We do not verify agaist trusted certificate (should be + // done by the caller if needed). + Path path = Paths.get(pkcs7SignaturePath); + if (Files.size(path) > MAX_SIGNATURE_FILE_SIZE_BYTES) { + throw new IllegalArgumentException("Signature size is unexpectedly large: " + + pkcs7SignaturePath); + } + final byte[] pkcs7Signature = Files.readAllBytes(path); + output.put(constructFsverityExtensionNative(kPkcs7SignatureExtensionId, + pkcs7Signature.length)); + output.put(pkcs7Signature); + + // 4. Generate the footer. + output.put(constructFsverityFooterNative(output.position() - origPosition)); + + return md.digest(); + } + + private static native byte[] constructFsverityDescriptorNative(long fileSize); + private static native byte[] constructFsverityExtensionNative(short extensionId, + int extensionDataSize); + private static native byte[] constructFsverityFooterNative(int offsetToDescriptorHead); + + /** * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has * length equals to the returned {@code Integer}. */ - private static Pair<SharedMemory, Integer> generateApkVerityIntoSharedMemory( - String apkPath, byte[] expectedRootHash) + private static Pair<SharedMemory, Integer> generateFsVerityIntoSharedMemory( + String apkPath, String signaturePath, @NonNull byte[] expectedRootHash, + boolean skipSigningBlock) throws IOException, SecurityException, DigestException, NoSuchAlgorithmException, SignatureNotFoundException { TrackedShmBufferFactory shmBufferFactory = new TrackedShmBufferFactory(); - byte[] generatedRootHash = ApkSignatureVerifier.generateApkVerity(apkPath, - shmBufferFactory); + byte[] generatedRootHash; + if (skipSigningBlock) { + generatedRootHash = ApkSignatureVerifier.generateApkVerity(apkPath, shmBufferFactory); + } else { + generatedRootHash = generateFsverityMetadata(apkPath, signaturePath, shmBufferFactory); + } // We only generate Merkle tree once here, so it's important to make sure the root hash // matches the signed one in the apk. if (!Arrays.equals(expectedRootHash, generatedRootHash)) { - throw new SecurityException("Locally generated verity root hash does not match"); + throw new SecurityException("verity hash mismatch: " + + bytesToString(generatedRootHash) + " != " + bytesToString(expectedRootHash)); } int contentSize = shmBufferFactory.getBufferLimit(); @@ -126,11 +252,15 @@ abstract public class VerityUtils { return Pair.create(shm, contentSize); } + private static String bytesToString(byte[] bytes) { + return HexEncoding.encodeToString(bytes); + } + public static class SetupResult { /** Result code if verity is set up correctly. */ private static final int RESULT_OK = 1; - /** Result code if the apk does not contain a verity root hash. */ + /** Result code if signature is not provided. */ private static final int RESULT_SKIPPED = 2; /** Result code if the setup failed. */ diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index c6e6449313c0..3c64dd278f78 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.IStatsManager; 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.os.storage.StorageManager; 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.internal.util.DumpUtils; 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.File; 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 class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { 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 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @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/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 1eae56745a75..e718c7b2a36e 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -20,8 +20,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.RemoteAnimationTarget.MODE_CLOSING; +import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; - import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.AnimationAdapterProto.REMOTE; @@ -48,16 +48,13 @@ import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; - import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; import com.android.server.input.InputWindowHandle; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; import com.android.server.wm.utils.InsetUtils; - import com.google.android.collect.Sets; - import java.io.PrintWriter; import java.util.ArrayList; @@ -93,6 +90,7 @@ public class RecentsAnimationController implements DeathRecipient { // The recents component app token that is shown behind the visibile tasks private AppWindowToken mTargetAppToken; + private int mTargetActivityType; private Rect mMinimizedHomeBounds = new Rect(); // We start the RecentsAnimationController in a pending-start state since we need to wait for @@ -259,23 +257,37 @@ public class RecentsAnimationController implements DeathRecipient { mDisplayId = displayId; } + public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) { + initialize(mService.mRoot.getDisplayContent(mDisplayId), targetActivityType, recentTaskIds); + } + /** * Initializes the recents animation controller. This is a separate call from the constructor * because it may call cancelAnimation() which needs to properly clean up the controller * in the window manager. */ - public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) { - // Make leashes for each of the visible tasks and add it to the recents animation to be - // started - final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); + @VisibleForTesting + void initialize(DisplayContent dc, int targetActivityType, SparseBooleanArray recentTaskIds) { + mTargetActivityType = targetActivityType; + + // Make leashes for each of the visible/target tasks and add it to the recents animation to + // be started final ArrayList<Task> visibleTasks = dc.getVisibleTasks(); + final TaskStack targetStack = dc.getStack(WINDOWING_MODE_UNDEFINED, targetActivityType); + if (targetStack != null) { + for (int i = targetStack.getChildCount() - 1; i >= 0; i--) { + final Task t = targetStack.getChildAt(i); + if (!visibleTasks.contains(t)) { + visibleTasks.add(t); + } + } + } final int taskCount = visibleTasks.size(); for (int i = 0; i < taskCount; i++) { final Task task = visibleTasks.get(i); final WindowConfiguration config = task.getWindowConfiguration(); if (config.tasksAreFloating() - || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || config.getActivityType() == targetActivityType) { + || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { continue; } addAnimation(task, !recentTaskIds.get(task.mTaskId)); @@ -586,7 +598,10 @@ public class RecentsAnimationController implements DeathRecipient { final Rect insets = new Rect(); mainWindow.getContentInsets(insets); InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets()); - mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash, + final int mode = topApp.getActivityType() == mTargetActivityType + ? MODE_OPENING + : MODE_CLOSING; + mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash, !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect, insets, mTask.getPrefixOrderIndex(), mPosition, mBounds, mTask.getWindowConfiguration(), mIsRecentTaskInvisible); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index cc23ab6f77d9..6aa0e0144c40 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -561,9 +561,10 @@ class Task extends WindowContainer<AppWindowToken> { @Override public SurfaceControl getAnimationLeashParent() { - // Reparent to the animation layer so that we aren't clipped by the non-minimized - // stack bounds, currently we only animate the task for the recents animation - return getAppAnimationLayer(ANIMATION_LAYER_STANDARD); + // Currently, only the recents animation will create animation leashes for tasks. In this + // case, reparent the task to the home animation layer while it is being animated to allow + // the home activity to reorder the app windows relative to its own. + return getAppAnimationLayer(ANIMATION_LAYER_HOME); } boolean isTaskAnimating() { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index e86093952474..4883f972f1e5 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -42,10 +42,8 @@ import android.view.MagnificationSpec; import android.view.SurfaceControl; import android.view.SurfaceControl.Builder; import android.view.SurfaceSession; - import com.android.internal.util.ToBooleanFunction; import com.android.server.wm.SurfaceAnimator.Animatable; - import java.io.PrintWriter; import java.util.Comparator; import java.util.LinkedList; @@ -71,7 +69,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< /** * Animation layer that is reserved for {@link WindowConfiguration#ACTIVITY_TYPE_HOME} - * activities that happens below all {@link TaskStack}s. + * activities and all activities that are being controlled by the recents animation. This + * layer is generally below all {@link TaskStack}s. */ static final int ANIMATION_LAYER_HOME = 2; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7caa7aedb873..b627df4a3313 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -227,6 +227,7 @@ import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; @@ -2714,6 +2715,11 @@ public class WindowManagerService extends IWindowManager.Stub } } + @VisibleForTesting + void setRecentsAnimationController(RecentsAnimationController controller) { + mRecentsAnimationController = controller; + } + public RecentsAnimationController getRecentsAnimationController() { return mRecentsAnimationController; } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index becde7311607..157b6349f4ee 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -37,6 +37,7 @@ cc_library_static { "com_android_server_locksettings_SyntheticPasswordManager.cpp", "com_android_server_net_NetworkStatsService.cpp", "com_android_server_power_PowerManagerService.cpp", + "com_android_server_security_VerityUtils.cpp", "com_android_server_SerialService.cpp", "com_android_server_storage_AppFuseBridge.cpp", "com_android_server_SystemServer.cpp", @@ -89,7 +90,6 @@ cc_defaults { "libsensorservicehidl", "libgui", "libusbhost", - "libsuspend", "libtinyalsa", "libEGL", "libGLESv2", @@ -121,6 +121,7 @@ cc_defaults { "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 02ad6c71b586..0ff60e44b0ce 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_0::Status; 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 @@ static sem_t wakeup_sem; 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 jgetSubsystem = NULL; 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 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) 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 b2d35d4153a0..0c9b5f4999a0 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::Void; 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 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t } } +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 nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean 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/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp new file mode 100644 index 000000000000..d0f173b572c8 --- /dev/null +++ b/services/core/jni/com_android_server_security_VerityUtils.cpp @@ -0,0 +1,137 @@ +/* + * 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. + */ + +#define LOG_TAG "VerityUtils" + +#include <nativehelper/JNIHelp.h> +#include "jni.h" +#include <utils/Log.h> + +#include <string.h> + +// TODO(112037636): Always include once fsverity.h is upstreamed and backported. +#define HAS_FSVERITY 0 + +#if HAS_FSVERITY +#include <linux/fsverity.h> +#endif + +namespace android { + +namespace { + +class JavaByteArrayHolder { + public: + static JavaByteArrayHolder* newArray(JNIEnv* env, jsize size) { + return new JavaByteArrayHolder(env, size); + } + + jbyte* getRaw() { + return mElements; + } + + jbyteArray release() { + mEnv->ReleaseByteArrayElements(mBytes, mElements, 0); + mElements = nullptr; + return mBytes; + } + + private: + JavaByteArrayHolder(JNIEnv* env, jsize size) { + mEnv = env; + mBytes = mEnv->NewByteArray(size); + mElements = mEnv->GetByteArrayElements(mBytes, nullptr); + memset(mElements, 0, size); + } + + virtual ~JavaByteArrayHolder() { + LOG_ALWAYS_FATAL_IF(mElements == nullptr, "Elements are not released"); + } + + JNIEnv* mEnv; + jbyteArray mBytes; + jbyte* mElements; +}; + +jbyteArray constructFsverityDescriptor(JNIEnv* env, jobject /* clazz */, jlong fileSize) { +#if HAS_FSVERITY + auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_descriptor)); + fsverity_descriptor* desc = reinterpret_cast<fsverity_descriptor*>(raii->getRaw()); + + memcpy(desc->magic, FS_VERITY_MAGIC, sizeof(desc->magic)); + desc->major_version = 1; + desc->minor_version = 0; + desc->log_data_blocksize = 12; + desc->log_tree_blocksize = 12; + desc->data_algorithm = FS_VERITY_ALG_SHA256; + desc->tree_algorithm = FS_VERITY_ALG_SHA256; + desc->flags = 0; + desc->orig_file_size = fileSize; + desc->auth_ext_count = 1; + + return raii->release(); +#else + LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); + return 0; +#endif // HAS_FSVERITY +} + +jbyteArray constructFsverityExtension(JNIEnv* env, jobject /* clazz */, jshort extensionId, + jint extensionDataSize) { +#if HAS_FSVERITY + auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_extension)); + fsverity_extension* ext = reinterpret_cast<fsverity_extension*>(raii->getRaw()); + + ext->length = sizeof(fsverity_extension) + extensionDataSize; + ext->type = extensionId; + + return raii->release(); +#else + LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); + return 0; +#endif // HAS_FSVERITY +} + +jbyteArray constructFsverityFooter(JNIEnv* env, jobject /* clazz */, + jint offsetToDescriptorHead) { +#if HAS_FSVERITY + auto raii = JavaByteArrayHolder::newArray(env, sizeof(fsverity_footer)); + fsverity_footer* footer = reinterpret_cast<fsverity_footer*>(raii->getRaw()); + + footer->desc_reverse_offset = offsetToDescriptorHead + sizeof(fsverity_footer); + memcpy(footer->magic, FS_VERITY_MAGIC, sizeof(footer->magic)); + + return raii->release(); +#else + LOG_ALWAYS_FATAL("fs-verity is used while not enabled"); + return 0; +#endif // HAS_FSVERITY +} + +const JNINativeMethod sMethods[] = { + { "constructFsverityDescriptorNative", "(J)[B", (void *)constructFsverityDescriptor }, + { "constructFsverityExtensionNative", "(SI)[B", (void *)constructFsverityExtension }, + { "constructFsverityFooterNative", "(I)[B", (void *)constructFsverityFooter }, +}; + +} // namespace + +int register_android_server_security_VerityUtils(JNIEnv* env) { + return jniRegisterNativeMethods(env, + "com/android/server/security/VerityUtils", sMethods, NELEM(sMethods)); +} + +} // namespace android diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index bb6e6840f3b4..918f57e2945e 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -54,6 +54,7 @@ int register_android_server_SyntheticPasswordManager(JNIEnv* env); int register_android_server_GraphicsStatsService(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); int register_android_server_net_NetworkStatsService(JNIEnv* env); +int register_android_server_security_VerityUtils(JNIEnv* env); }; using namespace android; @@ -101,5 +102,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_GraphicsStatsService(env); register_android_hardware_display_DisplayViewport(env); register_android_server_net_NetworkStatsService(env); + register_android_server_security_VerityUtils(env); return JNI_VERSION_1_4; } diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java index f7bf393f367b..5a73a4e492ee 100644 --- a/services/net/java/android/net/util/SharedLog.java +++ b/services/net/java/android/net/util/SharedLog.java @@ -17,6 +17,7 @@ package android.net.util; import android.annotation.NonNull; +import android.annotation.Nullable; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; @@ -92,10 +93,17 @@ public class SharedLog { } /** - * Log an error due to an exception, with the exception stacktrace. + * Log an error due to an exception, with the exception stacktrace if provided. + * + * <p>The error and exception message appear in the shared log, but the stacktrace is only + * logged in general log output (logcat). */ - public void e(@NonNull String msg, @NonNull Throwable e) { - Log.e(mTag, record(Category.ERROR, msg + ": " + e.getMessage()), e); + public void e(@NonNull String msg, @Nullable Throwable exception) { + if (exception == null) { + e(msg); + return; + } + Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception); } public void i(String msg) { diff --git a/services/robotests/src/com/android/server/backup/testing/TransportData.java b/services/robotests/src/com/android/server/backup/testing/TransportData.java index 4c67180050e2..77f5d9a48c18 100644 --- a/services/robotests/src/com/android/server/backup/testing/TransportData.java +++ b/services/robotests/src/com/android/server/backup/testing/TransportData.java @@ -48,9 +48,9 @@ public class TransportData { public static TransportData localTransport() { return new TransportData( - "android/com.android.internal.backup.LocalTransport", - "android/com.android.internal.backup.LocalTransportService", - "com.android.internal.backup.LocalTransport", + "com.android.localtransport/.LocalTransport", + "com.android.localtransport/.LocalTransportService", + "com.android.localtransport.LocalTransport", null, "Backing up to debug-only private cache", null, diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java index e7c45d59078c..aaa00452204b 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; @@ -24,6 +25,8 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_P import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; import static org.junit.Assert.fail; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.verify; @@ -33,6 +36,7 @@ import static org.mockito.Mockito.when; import android.os.Binder; import android.os.IInterface; import android.platform.test.annotations.Presubmit; +import android.util.SparseBooleanArray; import android.view.IRecentsAnimationRunner; import android.view.SurfaceControl; @@ -109,6 +113,24 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } } + @Test + public void testIncludedApps_expectTargetAndVisible() throws Exception { + sWm.setRecentsAnimationController(mController); + final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); + final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + hiddenAppWindow.setHidden(true); + mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray()); + + // Ensure that we are animating the target activity as well + assertTrue(mController.isAnimatingTask(homeAppWindow.getTask())); + assertTrue(mController.isAnimatingTask(appWindow.getTask())); + assertFalse(mController.isAnimatingTask(hiddenAppWindow.getTask())); + } + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder); 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 702161e48b75..13f3e5e7a3ad 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 @@ public class ZenModeHelperTest extends UiServiceTestCase { 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/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 995418ee706c..57b652e4cc59 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -271,6 +271,14 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { 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 8e99518d78b8..5e4518f67538 100644 --- a/telephony/java/android/telephony/NeighboringCellInfo.java +++ b/telephony/java/android/telephony/NeighboringCellInfo.java @@ -32,7 +32,11 @@ import android.os.Parcelable; /** * 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/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java index 7c7d7a0397ad..202da6817cb5 100644 --- a/telephony/java/android/telephony/NetworkScan.java +++ b/telephony/java/android/telephony/NetworkScan.java @@ -16,11 +16,10 @@ package android.telephony; +import android.annotation.IntDef; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; -import android.annotation.IntDef; -import android.util.Log; import com.android.internal.telephony.ITelephony; @@ -113,6 +112,8 @@ public class NetworkScan { } try { telephony.stopNetworkScan(mSubId, mScanId); + } catch (IllegalArgumentException ex) { + Rlog.d(TAG, "stopNetworkScan - no active scan for ScanID=" + mScanId); } catch (RemoteException ex) { Rlog.e(TAG, "stopNetworkScan RemoteException", ex); } catch (RuntimeException ex) { diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 38ee79f06690..d1091f489f14 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2153,7 +2153,12 @@ public class SubscriptionManager { /** * 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 824533d59bf5..ea9ac39c8111 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1590,6 +1590,7 @@ public class TelephonyManager { * * @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/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java index ae011a0316aa..c86f06eb88e4 100644 --- a/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java +++ b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java @@ -21,6 +21,7 @@ import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.result.ByteArrayInputStreamSource; import com.android.tradefed.result.ITestInvocationListener; import com.android.tradefed.result.LogDataType; +import com.android.tradefed.result.TestDescription; import com.android.tradefed.testtype.IDeviceTest; import com.android.tradefed.testtype.IRemoteTest; @@ -84,7 +85,10 @@ public class NativeProcessesMemoryTest implements IDeviceTest, IRemoteTest { // showmap requires root, we enable it here for the rest of the test mTestDevice.enableAdbRoot(); - listener.testRunStarted(RUN_NAME, 0 /* testCount */); + listener.testRunStarted(RUN_NAME, 1 /* testCount */); + + TestDescription testDescription = new TestDescription(getClass().getName(), "run"); + listener.testStarted(testDescription); // process name -> list of pids with that name Map<String, List<String>> nativeProcesses = collectNativeProcesses(); @@ -94,7 +98,8 @@ public class NativeProcessesMemoryTest implements IDeviceTest, IRemoteTest { mNativeProcessToMemory.put( NUM_NATIVE_PROCESSES_KEY, Integer.toString(nativeProcesses.size())); - listener.testRunEnded(0, mNativeProcessToMemory); + listener.testEnded(testDescription, mNativeProcessToMemory); + listener.testRunEnded(0, new HashMap<String, String>()); } /** Samples memory of all processes and logs the memory use. */ diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java index 39ecb7e5a45e..122edbaf078c 100644 --- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java +++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java @@ -69,17 +69,12 @@ public class InetDiagSocketTest { 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 @@ public class InetDiagSocketTest { 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/android/net/util/SharedLogTest.java b/tests/net/java/android/net/util/SharedLogTest.java index d46facfaba06..86048604e95f 100644 --- a/tests/net/java/android/net/util/SharedLogTest.java +++ b/tests/net/java/android/net/util/SharedLogTest.java @@ -44,6 +44,8 @@ public class SharedLogTest { final SharedLog logLevel2a = logTop.forSubComponent("twoA"); final SharedLog logLevel2b = logTop.forSubComponent("twoB"); logLevel2b.e("2b or not 2b"); + logLevel2b.e("No exception", null); + logLevel2b.e("Wait, here's one", new Exception("Test")); logLevel2a.w("second post?"); final SharedLog logLevel3 = logLevel2a.forSubComponent("three"); @@ -54,6 +56,9 @@ public class SharedLogTest { final String[] expected = { " - MARK first post!", " - [twoB] ERROR 2b or not 2b", + " - [twoB] ERROR No exception", + // No stacktrace in shared log, only in logcat + " - [twoB] ERROR Wait, here's one: Test", " - [twoA] WARN second post?", " - still logging", " - [twoA.three] 3 >> 2", diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index e3db7e8a1354..1a053057540f 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -79,6 +79,7 @@ import static org.mockito.Mockito.when; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -248,7 +249,7 @@ public class ConnectivityServiceTest { @Spy private Resources mResources; private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>(); - MockContext(Context base) { + MockContext(Context base, ContentProvider settingsProvider) { super(base); mResources = spy(base.getResources()); @@ -260,7 +261,7 @@ public class ConnectivityServiceTest { }); mContentResolver = new MockContentResolver(); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider); } @Override @@ -1048,7 +1049,9 @@ public class ConnectivityServiceTest { Looper.prepare(); } - mServiceContext = new MockContext(InstrumentationRegistry.getContext()); + FakeSettingsProvider.clearSettingsProvider(); + mServiceContext = new MockContext(InstrumentationRegistry.getContext(), + new FakeSettingsProvider()); LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class); LocalServices.addService( NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class)); @@ -1086,6 +1089,7 @@ public class ConnectivityServiceTest { mEthernetNetworkAgent.disconnect(); mEthernetNetworkAgent = null; } + FakeSettingsProvider.clearSettingsProvider(); } private static int transportToLegacyType(int transport) { @@ -4532,4 +4536,78 @@ public class ConnectivityServiceTest { 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 40d5544dccd8..a6ed9f252008 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_INTERFACE_NAME; 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.NetworkRequest; 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.net.wifi.WifiManager; 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 @@ public class TetheringTest { @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 class TetheringTest { public INetd getNetdService() { return mNetd; } + + @Override + public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, + DhcpServingParams params, SharedLog log) { + return mDhcpServer; + } }; } @@ -333,6 +344,7 @@ public class TetheringTest { 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 @@ public class TetheringTest { 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 @@ public class TetheringTest { 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 @@ public class TetheringTest { 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 @@ public class TetheringTest { 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 @@ public class TetheringTest { 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 @@ public class TetheringTest { // 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 bb312309b34f..521778484d91 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 @@ public class TetheringConfigurationTest { 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/tools/apilint/apilint.py b/tools/apilint/apilint.py index 934847f6eec2..91cd1cba964d 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -492,6 +492,7 @@ def verify_parcelable(clazz): def verify_protected(clazz): """Verify that no protected methods or fields are allowed.""" for m in clazz.methods: + if m.name == "finalize": continue if "protected" in m.split: error(clazz, m, "M7", "Protected methods not allowed; must be public") for f in clazz.fields: @@ -1025,6 +1026,10 @@ def verify_resource_names(clazz): # Resources defined by files are foo_bar_baz if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]: for f in clazz.fields: + if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue + if f.name.startswith("config_"): + error(clazz, f, None, "Expected config name to be config_fooBarBaz style") + if re.match("[a-z1-9_]+$", f.name): continue error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style") @@ -1361,6 +1366,60 @@ def verify_clone(clazz): error(clazz, m, None, "Provide an explicit copy constructor instead of implementing clone()") +def verify_pfd(clazz): + """Verify that android APIs use PFD over FD.""" + examine = clazz.ctors + clazz.methods + for m in examine: + if m.typ == "java.io.FileDescriptor": + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + if m.typ == "int": + if "Fd" in m.name or "FD" in m.name or "FileDescriptor" in m.name: + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + for arg in m.args: + if arg == "java.io.FileDescriptor": + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + + for f in clazz.fields: + if f.typ == "java.io.FileDescriptor": + error(clazz, f, "FW11", "Must use ParcelFileDescriptor") + + +def verify_numbers(clazz): + """Discourage small numbers types like short and byte.""" + + discouraged = ["short","byte"] + + for c in clazz.ctors: + for arg in c.args: + if arg in discouraged: + warn(clazz, c, "FW12", "Should avoid odd sized primitives; use int instead") + + for f in clazz.fields: + if f.typ in discouraged: + warn(clazz, f, "FW12", "Should avoid odd sized primitives; use int instead") + + for m in clazz.methods: + if m.typ in discouraged: + warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead") + for arg in m.args: + if arg in discouraged: + warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead") + + +def verify_singleton(clazz): + """Catch singleton objects with constructors.""" + + singleton = False + for m in clazz.methods: + if m.name.startswith("get") and m.name.endswith("Instance") and " static " in m.raw: + singleton = True + + if singleton: + for c in clazz.ctors: + error(clazz, c, None, "Singleton classes should use getInstance() methods") + + + def is_interesting(clazz): """Test if given class is interesting from an Android PoV.""" @@ -1431,6 +1490,9 @@ def examine_clazz(clazz): verify_tense(clazz) verify_icu(clazz) verify_clone(clazz) + verify_pfd(clazz) + verify_numbers(clazz) + verify_singleton(clazz) def examine_stream(stream): diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 991547916919..56c842805190 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -234,9 +234,11 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, } } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", const std::map<int, int64_t>& arg%d_1, " - "const std::map<int, char const*>& arg%d_2, " - "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex); + fprintf(out, ", const std::map<int, int32_t>& arg%d_1, " + "const std::map<int, int64_t>& arg%d_2, " + "const std::map<int, char const*>& arg%d_3, " + "const std::map<int, float>& arg%d_4", + argIndex, argIndex, argIndex, argIndex); } else { fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex); } @@ -302,6 +304,13 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, fprintf(out, " event.end();\n"); fprintf(out, " }\n"); + fprintf(out, " for (const auto& it : arg%d_4) {\n", argIndex); + fprintf(out, " event.begin();\n"); + fprintf(out, " event << it.first;\n"); + fprintf(out, " event << it.second;\n"); + fprintf(out, " event.end();\n"); + fprintf(out, " }\n"); + fprintf(out, " event.end();\n\n"); } else { if (*arg == JAVA_TYPE_STRING) { @@ -344,9 +353,11 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, } } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", const std::map<int, int64_t>& arg%d_1, " - "const std::map<int, char const*>& arg%d_2, " - "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex); + fprintf(out, ", const std::map<int, int32_t>& arg%d_1, " + "const std::map<int, int64_t>& arg%d_2, " + "const std::map<int, char const*>& arg%d_3, " + "const std::map<int, float>& arg%d_4", + argIndex, argIndex, argIndex, argIndex); } else { fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex); } @@ -374,7 +385,8 @@ static int write_stats_log_cpp(FILE *out, const Atoms &atoms, } } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", arg%d_1, arg%d_2, arg%d_3", argIndex, argIndex, argIndex); + fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", + argIndex, argIndex, argIndex, argIndex); } else { fprintf(out, ", arg%d", argIndex); } @@ -529,10 +541,14 @@ static void write_cpp_usage( } } } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", const std::map<int, int64_t>& %s_int" + fprintf(out, ", const std::map<int, int32_t>& %s_int" + ", const std::map<int, int64_t>& %s_long" ", const std::map<int, char const*>& %s_str" ", const std::map<int, float>& %s_float", - field->name.c_str(), field->name.c_str(), field->name.c_str()); + field->name.c_str(), + field->name.c_str(), + field->name.c_str(), + field->name.c_str()); } else { fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str()); } @@ -561,9 +577,11 @@ static void write_cpp_method_header( } } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", const std::map<int, int64_t>& arg%d_1, " - "const std::map<int, char const*>& arg%d_2, " - "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex); + fprintf(out, ", const std::map<int, int32_t>& arg%d_1, " + "const std::map<int, int64_t>& arg%d_2, " + "const std::map<int, char const*>& arg%d_3, " + "const std::map<int, float>& arg%d_4", + argIndex, argIndex, argIndex, argIndex); } else { fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex); } @@ -976,6 +994,7 @@ jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &att } static void write_key_value_map_jni(FILE* out) { + fprintf(out, " std::map<int, int32_t> int32_t_map;\n"); fprintf(out, " std::map<int, int64_t> int64_t_map;\n"); fprintf(out, " std::map<int, float> float_map;\n"); fprintf(out, " std::map<int, char const*> string_map;\n\n"); @@ -989,9 +1008,11 @@ static void write_key_value_map_jni(FILE* out) { fprintf(out, " std::vector<std::unique_ptr<ScopedUtfChars>> scoped_ufs;\n\n"); + fprintf(out, " jclass jint_class = env->FindClass(\"java/lang/Integer\");\n"); fprintf(out, " jclass jlong_class = env->FindClass(\"java/lang/Long\");\n"); fprintf(out, " jclass jfloat_class = env->FindClass(\"java/lang/Float\");\n"); fprintf(out, " jclass jstring_class = env->FindClass(\"java/lang/String\");\n"); + fprintf(out, " jmethodID jget_int_method = env->GetMethodID(jint_class, \"intValue\", \"()I\");\n"); fprintf(out, " jmethodID jget_long_method = env->GetMethodID(jlong_class, \"longValue\", \"()J\");\n"); fprintf(out, " jmethodID jget_float_method = env->GetMethodID(jfloat_class, \"floatValue\", \"()F\");\n\n"); @@ -1000,7 +1021,9 @@ static void write_key_value_map_jni(FILE* out) { fprintf(out, " jint key = env->CallIntMethod(value_map, jget_key_method, i);\n"); fprintf(out, " jobject jvalue_obj = env->CallObjectMethod(value_map, jget_value_method, i);\n"); fprintf(out, " if (jvalue_obj == NULL) { continue; }\n"); - fprintf(out, " if (env->IsInstanceOf(jvalue_obj, jlong_class)) {\n"); + fprintf(out, " if (env->IsInstanceOf(jvalue_obj, jint_class)) {\n"); + fprintf(out, " int32_t_map[key] = env->CallIntMethod(jvalue_obj, jget_int_method);\n"); + fprintf(out, " } else if (env->IsInstanceOf(jvalue_obj, jlong_class)) {\n"); fprintf(out, " int64_t_map[key] = env->CallLongMethod(jvalue_obj, jget_long_method);\n"); fprintf(out, " } else if (env->IsInstanceOf(jvalue_obj, jfloat_class)) {\n"); fprintf(out, " float_map[key] = env->CallFloatMethod(jvalue_obj, jget_float_method);\n"); @@ -1129,7 +1152,7 @@ write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp } } } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) { - fprintf(out, ", int64_t_map, string_map, float_map"); + fprintf(out, ", int32_t_map, int64_t_map, string_map, float_map"); } else { const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg"; fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex); diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index ce8d71d7ed2a..58c130017024 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -332,9 +332,10 @@ public class WifiConfiguration implements Parcelable { public String preSharedKey; /** - * Up to four WEP keys. Either an ASCII string enclosed in double - * quotation marks (e.g., {@code "abcdef"}) or a string - * of hex digits (e.g., {@code 0102030405}). + * Four WEP keys. For each of the four values, provide either an ASCII + * string enclosed in double quotation marks (e.g., {@code "abcdef"}), + * a string of hex digits (e.g., {@code 0102030405}), or an empty string + * (e.g., {@code ""}). * <p/> * When the value of one of these keys is read, the actual key is * not returned, just a "*" if the key has a value, or the null diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 7a91347102fe..59ba8e7a6177 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1698,9 +1698,7 @@ public class WifiManager { * @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 { |